From 431eeba05d93b786b63b152447f6bf625a87328b Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 14 Aug 2023 12:40:43 +0530 Subject: [PATCH 001/263] test mr build --- steps-to-test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/steps-to-test.txt b/steps-to-test.txt index 66e17f5a..1134464b 100644 --- a/steps-to-test.txt +++ b/steps-to-test.txt @@ -34,3 +34,4 @@ docker push localhost:5001/ubuntu:v1 # test commit +# test mr build From 49e67495ccdf5043ae7b504ad66f915df5469e30 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sat, 2 Sep 2023 22:21:08 +0530 Subject: [PATCH 002/263] updated client and agent --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 6 +- charts/client/Chart.yaml | 2 +- .../configmap-features-dashboard.yaml | 41 +- .../configmap-gitbridge-dashboard.yaml | 689 +++++++++++++++--- .../configmap-kubedata-dashboard.yaml | 25 +- .../templates/configmap-kubviz-dashboard.yaml | 455 ++++++++---- .../templates/configmap-trivy-dashboard.yaml | 188 +++-- charts/client/values.yaml | 2 +- 9 files changed, 1093 insertions(+), 317 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 64f8fa76..f11e80e4 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.0 +version: 1.0.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 6531a77c..cc67af35 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/kubviz-agent pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.0.0" + tag: "v1.1.0" imagePullSecrets: [] nameOverride: "" @@ -47,7 +47,7 @@ git_bridge: image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always - tag: "v1.0.0" + tag: "v1.1.0" resources: limits: cpu: 200m @@ -81,7 +81,7 @@ container_bridge: image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always - tag: "v1.0.0" + tag: "v1.1.0" resources: limits: cpu: 200m diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index edfa51cb..78c45a3d 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.2 +version: 1.0.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-features-dashboard.yaml b/charts/client/templates/configmap-features-dashboard.yaml index 031aa7aa..481e2472 100644 --- a/charts/client/templates/configmap-features-dashboard.yaml +++ b/charts/client/templates/configmap-features-dashboard.yaml @@ -30,7 +30,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 10, + "id": 5, "links": [], "liveNow": false, "panels": [ @@ -47,7 +47,9 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", + "cellOptions": { + "type": "color-text" + }, "filterable": true, "inspect": false }, @@ -76,7 +78,9 @@ data: }, "id": 8, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -85,7 +89,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -105,7 +109,6 @@ data: } ], "title": "Kubernetes Resources", - "transparent": true, "type": "table" }, { @@ -121,7 +124,9 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", + "cellOptions": { + "type": "color-text" + }, "filterable": true, "inspect": false }, @@ -150,7 +155,9 @@ data: }, "id": 6, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -159,7 +166,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -179,7 +186,6 @@ data: } ], "title": "Outdated Images", - "transparent": true, "type": "table" }, { @@ -195,7 +201,9 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", + "cellOptions": { + "type": "color-text" + }, "filterable": true, "inspect": false }, @@ -224,7 +232,9 @@ data: }, "id": 4, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -233,7 +243,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -253,7 +263,6 @@ data: } ], "title": "DeletedAPIs", - "transparent": true, "type": "table" }, { @@ -269,7 +278,9 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", + "cellOptions": { + "type": "color-text" + }, "filterable": true, "inspect": false }, @@ -298,7 +309,9 @@ data: }, "id": 2, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -307,7 +320,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -327,11 +340,11 @@ data: } ], "title": "DeprecatedAPIs", - "transparent": true, "type": "table" } ], - "schemaVersion": 37, + "refresh": "", + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { diff --git a/charts/client/templates/configmap-gitbridge-dashboard.yaml b/charts/client/templates/configmap-gitbridge-dashboard.yaml index d2917553..39ed92d2 100644 --- a/charts/client/templates/configmap-gitbridge-dashboard.yaml +++ b/charts/client/templates/configmap-gitbridge-dashboard.yaml @@ -30,7 +30,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 8, + "id": 9, "links": [], "liveNow": false, "panels": [ @@ -39,11 +39,10 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total number of merge request done from all the Git providers", "fieldConfig": { "defaults": { "color": { - "mode": "palette-classic" + "mode": "thresholds" }, "mappings": [], "thresholds": { @@ -52,10 +51,6 @@ data: { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -64,11 +59,11 @@ data: }, "gridPos": { "h": 5, - "w": 12, + "w": 5, "x": 0, "y": 0 }, - "id": 4, + "id": 42, "options": { "colorMode": "background", "graphMode": "area", @@ -83,7 +78,7 @@ data: }, "textMode": "auto" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -95,12 +90,63 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "GITHUB Push events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 5, + "y": 0 + }, + "id": 43, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -110,14 +156,64 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", - "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", - "refId": "B", + "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", + "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "GITLAB Push events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-blue", + "value": null + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 9, + "y": 0 + }, + "id": 44, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -127,14 +223,64 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", - "refId": "C", + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "BITBUCKET Push events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 14, + "y": 0 + }, + "id": 45, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -144,14 +290,64 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", - "refId": "D", + "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", + "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "GITEA Push events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-red", + "value": null + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 19, + "y": 0 + }, + "id": 46, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -161,16 +357,15 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", - "refId": "E", + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Number of Merge events from all Git providers", + "title": "AZURE Push events", "transparent": true, "type": "stat" }, @@ -179,12 +374,10 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total number push events triggered from different Git Providers", "fieldConfig": { "defaults": { "color": { - "fixedColor": "#7c766c", - "mode": "palette-classic" + "mode": "thresholds" }, "mappings": [], "thresholds": { @@ -193,10 +386,6 @@ data: { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -205,11 +394,11 @@ data: }, "gridPos": { "h": 5, - "w": 12, - "x": 12, - "y": 0 + "w": 5, + "x": 0, + "y": 5 }, - "id": 2, + "id": 37, "options": { "colorMode": "background", "graphMode": "area", @@ -224,7 +413,7 @@ data: }, "textMode": "auto" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -236,12 +425,63 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "GITHUB Merge events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 5, + "y": 5 + }, + "id": 38, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -251,14 +491,64 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", - "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", - "refId": "B", + "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", + "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "GITLAB Merge events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-blue", + "value": null + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 9, + "y": 5 + }, + "id": 39, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -268,14 +558,64 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", - "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", - "refId": "C", + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "BITBUCKET Merge events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 14, + "y": 5 + }, + "id": 40, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -285,14 +625,64 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", - "refId": "D", + "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "GITEA Merge events", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "light-red", + "value": null + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 19, + "y": 5 + }, + "id": 41, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -302,16 +692,15 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", - "refId": "E", + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Number of Push Events from all Git Providers", + "title": "AZURE Merge events", "transparent": true, "type": "stat" }, @@ -328,6 +717,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -341,6 +732,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -365,17 +759,19 @@ data: "h": 8, "w": 12, "x": 0, - "y": 5 + "y": 10 }, "id": 36, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -424,6 +820,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -437,6 +835,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -461,17 +862,19 @@ data: "h": 8, "w": 12, "x": 12, - "y": 5 + "y": 10 }, "id": 34, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -520,6 +923,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -533,6 +938,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -557,17 +965,19 @@ data: "h": 8, "w": 12, "x": 0, - "y": 13 + "y": 18 }, "id": 28, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -616,6 +1026,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -629,6 +1041,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -653,17 +1068,19 @@ data: "h": 8, "w": 12, "x": 12, - "y": 13 + "y": 18 }, "id": 30, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -712,6 +1129,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -725,6 +1144,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -749,17 +1171,19 @@ data: "h": 8, "w": 12, "x": 0, - "y": 21 + "y": 26 }, "id": 24, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -808,6 +1232,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -821,6 +1247,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -845,17 +1274,19 @@ data: "h": 8, "w": 12, "x": 12, - "y": 21 + "y": 26 }, "id": 26, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -917,6 +1348,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -930,6 +1363,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -954,17 +1390,19 @@ data: "h": 10, "w": 12, "x": 0, - "y": 29 + "y": 34 }, "id": 20, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -1013,6 +1451,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1026,6 +1466,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -1050,17 +1493,19 @@ data: "h": 10, "w": 12, "x": 12, - "y": 29 + "y": 34 }, "id": 22, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -1108,6 +1553,8 @@ data: "mode": "palette-classic" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "left", "axisSoftMin": 0, @@ -1121,6 +1568,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -1145,17 +1595,19 @@ data: "h": 10, "w": 12, "x": 0, - "y": 39 + "y": 44 }, "id": 16, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "table", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -1203,6 +1655,8 @@ data: "mode": "palette-classic" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1216,6 +1670,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "mappings": [], @@ -1240,17 +1697,19 @@ data: "h": 10, "w": 12, "x": 12, - "y": 39 + "y": 44 }, "id": 18, "options": { "barRadius": 0.2, "barWidth": 0.3, + "fullHighlight": false, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", - "placement": "bottom" + "placement": "bottom", + "showLegend": true }, "orientation": "auto", "showValue": "always", @@ -1299,8 +1758,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -1323,11 +1785,13 @@ data: "h": 7, "w": 24, "x": 0, - "y": 49 + "y": 54 }, "id": 14, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -1336,7 +1800,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -1388,8 +1852,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -1412,11 +1879,13 @@ data: "h": 8, "w": 24, "x": 0, - "y": 56 + "y": 61 }, "id": 12, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -1425,7 +1894,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -1477,8 +1946,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -1501,11 +1973,13 @@ data: "h": 9, "w": 24, "x": 0, - "y": 64 + "y": 69 }, "id": 10, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -1514,7 +1988,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -1566,8 +2040,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -1590,11 +2067,13 @@ data: "h": 8, "w": 24, "x": 0, - "y": 73 + "y": 78 }, "id": 8, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -1603,7 +2082,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -1655,8 +2134,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -1679,11 +2161,13 @@ data: "h": 9, "w": 24, "x": 0, - "y": 81 + "y": 86 }, "id": 6, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -1692,7 +2176,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -1732,7 +2216,8 @@ data: "type": "table" } ], - "schemaVersion": 35, + "refresh": "", + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { @@ -1746,7 +2231,7 @@ data: "timezone": "", "title": "GitBridge", "uid": "u3EJcUqVk", - "version": 15, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubedata-dashboard.yaml b/charts/client/templates/configmap-kubedata-dashboard.yaml index f68b63e1..af1e7eff 100644 --- a/charts/client/templates/configmap-kubedata-dashboard.yaml +++ b/charts/client/templates/configmap-kubedata-dashboard.yaml @@ -30,7 +30,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 15, + "id": 1, "links": [], "liveNow": false, "panels": [ @@ -84,7 +84,7 @@ data: }, "textMode": "auto" }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -96,8 +96,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT\n count()\nFROM default.events\n\nWHERE Kind IN ('Pod') AND Reason IN ('Created') AND Namespace IN ($namespace)\n", - "rawQuery": "SELECT\n count()\nFROM default.events\n\nWHERE Kind IN ('Pod') AND Reason IN ('Created') AND Namespace IN ('argocd','quality','default','sonarqube','observability')", + "query": "SELECT\n count()\nFROM default.events\n\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('Pod') AND Reason IN ('Created') AND Namespace IN ($namespace)\n", + "rawQuery": "SELECT\n count()\nFROM default.events\n\nWHERE EventTime >= toDateTime(1693485465) AND EventTime <= toDateTime(1693486365) AND Kind IN ('Pod') AND Reason IN ('Created') AND Namespace IN ('argocd','kubviz','default','observability','otel-collector','tek','sonarqube','tekton-pipelines','cert-manager','kube-system','quality','traefik','tracetestdemo')", "refId": "A", "round": "0s", "skip_comments": true @@ -119,7 +119,9 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", + "cellOptions": { + "type": "color-text" + }, "filterable": true, "inspect": false }, @@ -148,7 +150,9 @@ data: }, "id": 2, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -157,7 +161,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -169,8 +173,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.events\nWHERE Namespace IN ($namespace) AND Reason IN ($reason) AND Kind IN ($kind) AND ClusterName IN ($clusterName)", - "rawQuery": "SELECT * FROM default.events\nWHERE Namespace IN ('argocd','quality','default','sonarqube','observability') AND Reason IN ('OperationStarted','ResourceUpdated','OperationCompleted','FailedMount','ApplyClusterRoles','ApplyRoles','UpdateFailed','Unhealthy','InstallPackageRevision','Valid','Updated','RenderCRD','BackOff','BindClusterRole','SyncPackage') AND Kind IN ('Application','Pod','ProviderRevision','CompositeResourceDefinition','Namespace','ExternalSecret','Provider','SecretStore') AND ClusterName IN ('kubviz')", + "query": "SELECT * FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Namespace IN ($namespace) AND Reason IN ($reason) AND Kind IN ($kind) AND ClusterName IN ($clusterName)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.events\nWHERE EventTime >= toDateTime(1693486140) AND EventTime <= toDateTime(1693486440) AND Namespace IN ('argocd','kubviz','default','observability','otel-collector','tek','sonarqube','tekton-pipelines','cert-manager','kube-system','quality','traefik','tracetestdemo') AND Reason IN ('OperationCompleted','Pulled','Created','Started','OperationStarted','SuccessfulCreate','Scheduled','BackOff','Pulling','Unhealthy','Killing','FinalizerUpdate','WaitForFirstConsumer','Running','ExternalProvisioning','Provisioning','Pending','ProvisioningFailed','ProvisioningSucceeded','SuccessfulAttachVolume','Succeeded','Failed','FailedGetResourceMetric','FailedComputeMetricsReplicas','NodeNotReady','NodeHasSufficientMemory','NodeHasNoDiskPressure','NodeHasSufficientPID','NodeReady') AND Kind IN ('Application','Pod','ReplicaSet','StatefulSet','PipelineRun','PersistentVolumeClaim','TaskRun','HorizontalPodAutoscaler','Node') AND ClusterName IN ('dev')\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -181,7 +185,8 @@ data: "type": "table" } ], - "schemaVersion": 37, + "refresh": "", + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { @@ -281,7 +286,7 @@ data: ] }, "time": { - "from": "now-15m", + "from": "now-5m", "to": "now" }, "timepicker": {}, diff --git a/charts/client/templates/configmap-kubviz-dashboard.yaml b/charts/client/templates/configmap-kubviz-dashboard.yaml index 888a7ec0..c2700f00 100644 --- a/charts/client/templates/configmap-kubviz-dashboard.yaml +++ b/charts/client/templates/configmap-kubviz-dashboard.yaml @@ -30,7 +30,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, + "id": 1, "links": [], "liveNow": false, "panels": [ @@ -47,8 +47,12 @@ data: }, "custom": { "align": "center", - "displayMode": "lcd-gauge", - "filterable": true + "cellOptions": { + "mode": "lcd", + "type": "gauge" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -86,7 +90,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -98,15 +102,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT DISTINCT ClusterName from(SELECT distinct ClusterName FROM default.DeletedAPIs\nUNION ALL\nSELECT DISTINCT ClusterName FROM default.DeprecatedAPIs\n UNION ALL\n SELECT DISTINCT ClusterName FROM default.events\n UNION ALL\n SELECT DISTINCT ClusterName FROM default.getall_resources\n UNION ALL\n SELECT DISTINCT ClusterName FROM default.outdated_images)", - "rawQuery": "SELECT DISTINCT ClusterName from(SELECT distinct ClusterName FROM default.DeletedAPIs\nUNION ALL\nSELECT DISTINCT ClusterName FROM default.DeprecatedAPIs\n UNION ALL\n SELECT DISTINCT ClusterName FROM default.events\n UNION ALL\n SELECT DISTINCT ClusterName FROM default.getall_resources\n UNION ALL\n SELECT DISTINCT ClusterName FROM default.outdated_images)", + "query": "SELECT DISTINCT ClusterName FROM default.events", + "rawQuery": "SELECT DISTINCT ClusterName FROM default.events", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Monitoring Clusters", - "transparent": true, "type": "table" }, { @@ -163,7 +166,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -175,15 +178,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName,count(*) AS Events From default.events\nGROUP BY ClusterName\nORDER BY Events DESC\nLIMIT 1", - "rawQuery": "SELECT ClusterName,count(*) AS Events From default.events\nGROUP BY ClusterName\nORDER BY Events DESC\nLIMIT 1", + "query": "SELECT ClusterName,count(*) AS Events From default.events\nWHERE $timeFilterByColumn(EventTime) \nGROUP BY ClusterName\nORDER BY Events DESC\nLIMIT 1", + "rawQuery": "SELECT ClusterName,count(*) AS Events From default.events\nWHERE EventTime >= toDateTime(1693460315) AND EventTime <= toDateTime(1693460615) \nGROUP BY ClusterName\nORDER BY Events DESC\nLIMIT 1", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Clusters with Most Activity", - "transparent": true, "type": "gauge" }, { @@ -240,7 +242,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -260,7 +262,6 @@ data: } ], "title": "Number of Outdated Images Across All Clusters", - "transparent": true, "type": "gauge" }, { @@ -317,7 +318,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -329,15 +330,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events", - "rawQuery": "SELECT count(*) FROM default.events", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) ", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693460292) AND EventTime <= toDateTime(1693460592)", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of Events across all Clusters", - "transparent": true, "type": "gauge" }, { @@ -394,7 +394,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -414,7 +414,6 @@ data: } ], "title": "Number of DeletedAPIs across all Clusters", - "transparent": true, "type": "gauge" }, { @@ -471,7 +470,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -491,7 +490,6 @@ data: } ], "title": "Number of DeprecatedAPIs across all Clusters", - "transparent": true, "type": "gauge" }, { @@ -548,7 +546,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -568,9 +566,131 @@ data: } ], "title": "Number of k8s_Resources across all Clusters", - "transparent": true, "type": "gauge" }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "description": "This panel provides a time-based analysis of the occurrences of 'Pod' and 'Node'", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisGridShow": true, + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 53, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "format": 0, + "meta": { + "builderOptions": { + "fields": [], + "limit": 100, + "mode": "list" + } + }, + "queryType": "sql", + "rawSql": "SELECT EventTime, COUNT(*) AS Pods\nFROM default.events\nWHERE Kind = 'Pod'\nGROUP BY EventTime;\n", + "refId": "A", + "selectedFormat": 0 + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "format": 0, + "hide": false, + "meta": { + "builderOptions": { + "fields": [], + "limit": 100, + "mode": "list" + } + }, + "queryType": "sql", + "rawSql": "SELECT EventTime, COUNT(*) AS Nodes\nFROM default.events\nWHERE Kind = 'Node'\nGROUP BY EventTime;\n", + "refId": "B", + "selectedFormat": 0 + } + ], + "title": "Number of Pods and Nodes over time", + "type": "timeseries" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -611,7 +731,7 @@ data: "h": 4, "w": 6, "x": 0, - "y": 10 + "y": 16 }, "id": 57, "options": { @@ -628,7 +748,7 @@ data: }, "textMode": "value" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -640,8 +760,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693438853) AND EventTime <= toDateTime(1693460453) AND Kind IN 'Pod'", "refId": "A", "round": "0s", "skip_comments": true @@ -690,7 +810,7 @@ data: "h": 4, "w": 6, "x": 6, - "y": 10 + "y": 16 }, "id": 63, "options": { @@ -705,7 +825,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -717,15 +837,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod' AND Reason IN 'Created'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod' AND Reason IN 'Created'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' AND Reason IN 'Created'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693460172) AND EventTime <= toDateTime(1693460472) AND Kind IN 'Pod' AND Reason IN 'Created'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of pods with Created state", - "transparent": true, "type": "gauge" }, { @@ -768,7 +887,7 @@ data: "h": 4, "w": 6, "x": 12, - "y": 10 + "y": 16 }, "id": 61, "options": { @@ -783,7 +902,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -795,15 +914,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod' AND Reason IN 'BackOff'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod' AND Reason IN 'BackOff'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' AND Reason IN 'BackOff'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693460182) AND EventTime <= toDateTime(1693460482) AND Kind IN 'Pod' AND Reason IN 'BackOff'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of pods with BackOff state", - "transparent": true, "type": "gauge" }, { @@ -846,7 +964,7 @@ data: "h": 4, "w": 6, "x": 18, - "y": 10 + "y": 16 }, "id": 62, "options": { @@ -861,7 +979,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -873,15 +991,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod' AND Reason IN 'Unhealthy'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Pod' AND Reason IN 'Unhealthy'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' AND Reason IN 'Unhealthy'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693460193) AND EventTime <= toDateTime(1693460493) AND Kind IN 'Pod' AND Reason IN 'Unhealthy'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of pods with Unhealthy state", - "transparent": true, "type": "gauge" }, { @@ -924,7 +1041,7 @@ data: "h": 4, "w": 6, "x": 0, - "y": 14 + "y": 20 }, "id": 56, "options": { @@ -941,7 +1058,7 @@ data: }, "textMode": "value" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -953,15 +1070,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Node'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693438754) AND EventTime <= toDateTime(1693460354) AND Kind IN 'Node'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of Node creation events", - "transparent": true, "type": "stat" }, { @@ -1004,7 +1120,7 @@ data: "h": 4, "w": 6, "x": 6, - "y": 14 + "y": 20 }, "id": 58, "options": { @@ -1019,7 +1135,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -1031,15 +1147,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node' AND Reason IN 'NodeNotReady'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node' AND Reason IN 'NodeNotReady'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Node' AND Reason IN 'NodeNotReady'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693438784) AND EventTime <= toDateTime(1693460384) AND Kind IN 'Node' AND Reason IN 'NodeNotReady'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of Nodes with NotReady State", - "transparent": true, "type": "gauge" }, { @@ -1082,7 +1197,7 @@ data: "h": 4, "w": 6, "x": 12, - "y": 14 + "y": 20 }, "id": 59, "options": { @@ -1097,7 +1212,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -1109,15 +1224,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node' AND Reason IN 'NodeReady'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node' AND Reason IN 'NodeReady'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Node' AND Reason IN 'NodeReady'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693438825) AND EventTime <= toDateTime(1693460425) AND Kind IN 'Node' AND Reason IN 'NodeReady'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of Nodes with Ready State", - "transparent": true, "type": "gauge" }, { @@ -1160,7 +1274,7 @@ data: "h": 4, "w": 6, "x": 18, - "y": 14 + "y": 20 }, "id": 60, "options": { @@ -1175,7 +1289,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -1187,15 +1301,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node' AND Reason IN 'NodeHasNoDiskPressure'", - "rawQuery": "SELECT count(*) FROM default.events\nWHERE Kind IN 'Node' AND Reason IN 'NodeHasNoDiskPressure'", + "query": "SELECT count(*) FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Node' AND Reason IN 'NodeHasNoDiskPressure'", + "rawQuery": "SELECT count(*) FROM default.events\nWHERE EventTime >= toDateTime(1693438835) AND EventTime <= toDateTime(1693460435) AND Kind IN 'Node' AND Reason IN 'NodeHasNoDiskPressure'", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Number of Nodes with NodeHasNoDiskPressure State", - "transparent": true, "type": "gauge" }, { @@ -1211,8 +1324,11 @@ data: }, "custom": { "align": "center", - "displayMode": "gradient-gauge", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "links": [ { @@ -1248,12 +1364,20 @@ data: { "matcher": { "id": "byName", - "options": "Namespace" + "options": "Events" }, "properties": [ { "id": "custom.width", "value": 425 + }, + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "gauge", + "valueDisplayMode": "text" + } } ] } @@ -1263,7 +1387,7 @@ data: "h": 8, "w": 24, "x": 0, - "y": 18 + "y": 24 }, "id": 44, "options": { @@ -1279,7 +1403,7 @@ data: "showHeader": true, "sortBy": [] }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -1291,15 +1415,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Namespace, count(Event) AS Events FROM default.events\nGROUP BY ClusterName,Namespace\nORDER BY Events DESC", - "rawQuery": "SELECT ClusterName, Namespace, count(Event) AS Events FROM default.events\nGROUP BY ClusterName,Namespace\nORDER BY Events DESC", + "query": "SELECT ClusterName, Namespace, count(Event) AS Events FROM default.events\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName,Namespace\nORDER BY Events DESC", + "rawQuery": "SELECT ClusterName, Namespace, count(Event) AS Events FROM default.events\nWHERE EventTime >= toDateTime(1693460424) AND EventTime <= toDateTime(1693460724)\nGROUP BY ClusterName,Namespace\nORDER BY Events DESC", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Events per cluster by Namespace", - "transparent": true, "type": "table" }, { @@ -1335,7 +1458,7 @@ data: "h": 5, "w": 24, "x": 0, - "y": 26 + "y": 32 }, "id": 40, "options": { @@ -1350,7 +1473,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -1421,7 +1544,6 @@ data: } ], "title": "Number of Clusters Containing Activity", - "transparent": true, "type": "gauge" }, { @@ -1435,6 +1557,14 @@ data: "color": { "mode": "continuous-GrYlRd" }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, "links": [ { "targetBlank": true, @@ -1447,8 +1577,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1457,29 +1586,45 @@ data: ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Pods" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge", + "valueDisplayMode": "text" + } + } + ] + } + ] }, "gridPos": { "h": 8, "w": 24, "x": 0, - "y": 31 + "y": 37 }, "id": 64, "options": { - "displayMode": "lcd", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -1491,16 +1636,15 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(Kind) AS Pods,ClusterName, Reason FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", - "rawQuery": "SELECT count(Kind) AS Pods,ClusterName, Reason FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Count of Pods grouped by Reason", - "transparent": true, - "type": "bargauge" + "type": "table" }, { "datasource": { @@ -1515,6 +1659,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1528,6 +1674,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "links": [ @@ -1542,8 +1691,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1558,7 +1706,7 @@ data: "h": 9, "w": 12, "x": 0, - "y": 39 + "y": 45 }, "id": 34, "options": { @@ -1601,7 +1749,6 @@ data: } ], "title": "Count of DeprecatedAPIs per cluster", - "transparent": true, "type": "barchart" }, { @@ -1617,6 +1764,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1630,6 +1779,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "links": [ @@ -1644,8 +1796,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1660,7 +1811,7 @@ data: "h": 9, "w": 12, "x": 12, - "y": 39 + "y": 45 }, "id": 36, "options": { @@ -1703,7 +1854,6 @@ data: } ], "title": "Count of DeletedAPIs per cluster", - "transparent": true, "type": "barchart" }, { @@ -1719,6 +1869,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1732,6 +1884,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "links": [ @@ -1746,8 +1901,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1762,7 +1916,7 @@ data: "h": 8, "w": 8, "x": 0, - "y": 48 + "y": 54 }, "id": 28, "options": { @@ -1805,7 +1959,6 @@ data: } ], "title": "Count of Outdated Images per cluster", - "transparent": true, "type": "barchart" }, { @@ -1820,6 +1973,8 @@ data: "mode": "palette-classic" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1833,6 +1988,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "links": [ @@ -1847,8 +2005,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1863,7 +2020,7 @@ data: "h": 8, "w": 8, "x": 8, - "y": 48 + "y": 54 }, "id": 32, "options": { @@ -1898,15 +2055,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "select ClusterName,count(Id) AS Events from default.events\ngroup by ClusterName", - "rawQuery": "select ClusterName,count(Id) AS Events from default.events\ngroup by ClusterName", + "query": "select ClusterName,count(Id) AS Events from default.events\nWHERE $timeFilterByColumn(EventTime) \ngroup by ClusterName", + "rawQuery": "select ClusterName,count(Id) AS Events from default.events\nWHERE EventTime >= toDateTime(1693460253) AND EventTime <= toDateTime(1693460553) \ngroup by ClusterName", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Count of Events per cluster", - "transparent": true, "type": "barchart" }, { @@ -1922,6 +2078,8 @@ data: "mode": "fixed" }, "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "axisSoftMin": 0, @@ -1935,6 +2093,9 @@ data: "lineWidth": 1, "scaleDistribution": { "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" } }, "links": [ @@ -1949,8 +2110,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1965,7 +2125,7 @@ data: "h": 8, "w": 8, "x": 16, - "y": 48 + "y": 54 }, "id": 30, "options": { @@ -2008,7 +2168,6 @@ data: } ], "title": "Count of Resources per cluster", - "transparent": true, "type": "barchart" }, { @@ -2024,8 +2183,11 @@ data: }, "custom": { "align": "center", - "displayMode": "gradient-gauge", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "links": [ { @@ -2039,8 +2201,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "light-yellow", @@ -2053,13 +2214,30 @@ data: ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Resources" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "gradient", + "type": "gauge", + "valueDisplayMode": "text" + } + } + ] + } + ] }, "gridPos": { "h": 7, "w": 24, "x": 0, - "y": 56 + "y": 62 }, "id": 42, "options": { @@ -2074,7 +2252,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2094,20 +2272,19 @@ data: } ], "title": " Resources per Cluster by Kind", - "transparent": true, "type": "table" }, { "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 63 + "y": 69 }, "id": 16, "panels": [ @@ -2150,11 +2327,13 @@ data: "h": 16, "w": 24, "x": 0, - "y": 217 + "y": 70 }, "id": 14, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2163,7 +2342,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2191,7 +2370,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "refId": "A" } @@ -2203,13 +2382,13 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 64 + "y": 70 }, "id": 12, "panels": [ @@ -2292,7 +2471,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "refId": "A" } @@ -2304,13 +2483,13 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 65 + "y": 71 }, "id": 8, "panels": [ @@ -2394,7 +2573,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "refId": "A" } @@ -2406,13 +2585,13 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 66 + "y": 72 }, "id": 4, "panels": [ @@ -2496,7 +2675,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "e06865c2-5bcc-4533-8de7-880298c555af" }, "refId": "A" } @@ -2506,21 +2685,21 @@ data: } ], "refresh": "", - "schemaVersion": 35, + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-6h", + "from": "now-30m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 8, + "version": 4, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 9e3c1921..1b1dcc41 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -30,7 +30,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 6, + "id": 4, "links": [], "liveNow": false, "panels": [ @@ -71,15 +71,18 @@ data: "id": 20, "options": { "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { "calcs": [], "fields": "", "values": true }, - "showUnfilled": true + "showUnfilled": true, + "valueMode": "color" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -99,7 +102,6 @@ data: } ], "title": "Vulnerability Severity counts grouped by Cluster", - "transparent": true, "type": "bargauge" }, { @@ -139,15 +141,18 @@ data: "id": 22, "options": { "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { "calcs": [], "fields": "", "values": true }, - "showUnfilled": true + "showUnfilled": true, + "valueMode": "color" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -167,7 +172,6 @@ data: } ], "title": "Misconfiguration Severity counts grouped by Cluster", - "transparent": true, "type": "bargauge" }, { @@ -216,7 +220,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -236,7 +240,6 @@ data: } ], "title": "Misconfiguration Count by Cluster", - "transparent": true, "type": "gauge" }, { @@ -285,7 +288,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -305,7 +308,6 @@ data: } ], "title": "Vulnerability Count by Cluster", - "transparent": true, "type": "gauge" }, { @@ -345,15 +347,18 @@ data: "id": 12, "options": { "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { "calcs": [], "fields": "", "values": true }, - "showUnfilled": true + "showUnfilled": true, + "valueMode": "color" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -373,7 +378,6 @@ data: } ], "title": "Critical Vulnerability Count by Namespace and ClusterName", - "transparent": true, "type": "bargauge" }, { @@ -413,15 +417,18 @@ data: "id": 14, "options": { "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { "calcs": [], "fields": "", "values": true }, - "showUnfilled": true + "showUnfilled": true, + "valueMode": "color" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -442,7 +449,6 @@ data: } ], "title": "Critical Misconfiguration Count by Namespace and ClusterName", - "transparent": true, "type": "bargauge" }, { @@ -493,7 +499,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -564,7 +570,6 @@ data: } ], "title": "Count of Misconfiguration Severity Level", - "transparent": true, "type": "gauge" }, { @@ -615,7 +620,7 @@ data: "showThresholdLabels": false, "showThresholdMarkers": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -686,7 +691,6 @@ data: } ], "title": "Count of Vulnereability Severity level", - "transparent": true, "type": "gauge" }, { @@ -726,15 +730,18 @@ data: "id": 4, "options": { "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { "calcs": [], "fields": "", "values": true }, - "showUnfilled": true + "showUnfilled": true, + "valueMode": "color" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -754,7 +761,6 @@ data: } ], "title": "Misconfiguration Count by Cluster and Namespace", - "transparent": true, "type": "bargauge" }, { @@ -794,15 +800,18 @@ data: "id": 6, "options": { "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { "calcs": [], "fields": "", "values": true }, - "showUnfilled": true + "showUnfilled": true, + "valueMode": "color" }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -822,7 +831,6 @@ data: } ], "title": "Vulnerability Count by Cluster and Namespace", - "transparent": true, "type": "bargauge" }, { @@ -837,8 +845,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -876,7 +887,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -899,7 +910,7 @@ data: "vul_last_modified_date" ], "filters": [], - "limit": 100, + "limit": null, "mode": "list", "orderBy": [], "table": "trivy_vul" @@ -910,13 +921,12 @@ data: }, "format": 1, "queryType": "builder", - "rawSql": "SELECT cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date FROM default.\"trivy_vul\" LIMIT 100", + "rawSql": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" FROM \"default\".\"trivy_vul\"", "refId": "A", "selectedFormat": 1 } ], "title": "Trivy Vulnerabilities", - "transparent": true, "type": "table" }, { @@ -932,8 +942,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -971,7 +984,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -993,7 +1006,7 @@ data: "misconfig_status" ], "filters": [], - "limit": 100, + "limit": null, "mode": "list", "orderBy": [], "table": "trivy_misconfig" @@ -1004,13 +1017,12 @@ data: }, "format": 1, "queryType": "builder", - "rawSql": "SELECT cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status FROM default.\"trivy_misconfig\" LIMIT 100", + "rawSql": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" FROM \"default\".\"trivy_misconfig\"", "refId": "A", "selectedFormat": 1 } ], "title": "Trivy Misconfiguration", - "transparent": true, "type": "table" }, { @@ -1025,8 +1037,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -1053,7 +1068,9 @@ data: }, "id": 24, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -1062,7 +1079,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "builderOptions": { @@ -1117,7 +1134,7 @@ data: "vul_last_modified_date" ], "filters": [], - "limit": 100, + "limit": null, "mode": "list", "orderBy": [], "table": "trivyimage" @@ -1135,18 +1152,95 @@ data: } }, "queryType": "builder", - "rawSql": "SELECT cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date FROM default.\"trivyimage\" LIMIT 100", + "rawSql": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" FROM \"default\".\"trivyimage\"", "refId": "A", "selectedFormat": 1 } ], "title": "Trivy Image", - "transparent": true, + "type": "table" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 62 + }, + "id": 25, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "builderOptions": { + "database": "default", + "fields": [ + "*" + ], + "filters": [], + "limit": null, + "mode": "list", + "orderBy": [], + "table": "trivysbom" + }, + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "format": 1, + "queryType": "builder", + "rawSql": "SELECT * FROM \"default\".\"trivysbom\"", + "refId": "A", + "selectedFormat": 1 + } + ], + "title": "Trivy_SBOM", "type": "table" } ], "refresh": "", - "schemaVersion": 35, + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { @@ -1160,7 +1254,7 @@ data: "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d47", - "version": 15, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/values.yaml b/charts/client/values.yaml index d0231221..ee6c23d7 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/client pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.0.0" + tag: "v1.1.0" imagePullSecrets: [] nameOverride: "" From 624a77ead6c276bb201cf37bee1fea4e46f00797 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sat, 2 Sep 2023 22:26:08 +0530 Subject: [PATCH 003/263] update chart version --- charts/agent/Chart.yaml | 2 +- charts/client/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index f11e80e4..c5769556 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.1 +version: 1.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 78c45a3d..7caa1d5b 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.3 +version: 1.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to From 778e60d3b5dda87dfa30c90494735b7799dfb0f4 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 4 Sep 2023 15:39:57 +0530 Subject: [PATCH 004/263] test image build --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bc19e09b..8bb71814 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ civo +migration/ From b1afc3d90337ecc37dd605bfbc71faee3b81e14a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 4 Sep 2023 20:54:23 +0530 Subject: [PATCH 005/263] parallel to sync --- agent/kubviz/k8smetrics_agent.go | 125 +++++++++---------------------- agent/kubviz/ketall.go | 20 ++--- agent/kubviz/kubePreUpgrade.go | 17 ++--- agent/kubviz/kube_score.go | 23 +++--- agent/kubviz/outdated.go | 12 +-- agent/kubviz/rakees_agent.go | 25 +++---- agent/kubviz/trivy.go | 19 +++-- agent/kubviz/trivy_image.go | 16 ++-- agent/kubviz/trivy_sbom.go | 11 +-- 9 files changed, 104 insertions(+), 164 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 7f28d0a3..3a6f8479 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -6,7 +6,6 @@ import ( "os" "strconv" "strings" - "sync" "time" "github.com/go-co-op/gocron" @@ -57,11 +56,21 @@ var ( schedulingIntervalStr string = os.Getenv("SCHEDULING_INTERVAL") ) -func runTrivyScans(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGroup, trivyImagescanChan, trivySbomcanChan, trivyK8sMetricsChan chan error) { - RunTrivyK8sClusterScan(js, trivyK8sMetricsChan) - RunTrivyImageScans(config, js, trivyImagescanChan) - RunTrivySbomScan(config, js, trivySbomcanChan) - wg.Done() +func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { + err := RunTrivyK8sClusterScan(js) + if err != nil { + return err + } + err = RunTrivyImageScans(config, js) + if err != nil { + return err + } + err = RunTrivySbomScan(config, js) + if err != nil { + return err + } + return nil + } func main() { @@ -69,7 +78,6 @@ func main() { env := Production clusterMetricsChan := make(chan error, 1) var ( - wg sync.WaitGroup config *rest.Config clientset *kubernetes.Clientset ) @@ -100,90 +108,22 @@ func main() { // starting the endless go routine to monitor the cluster go publishMetrics(clientset, js, clusterMetricsChan) - // starting all the go routines collectAndPublishMetrics := func() { - // error channels declared for the go routines - outdatedErrChan := make(chan error, 1) - kubePreUpgradeChan := make(chan error, 1) - getAllResourceChan := make(chan error, 1) - trivyK8sMetricsChan := make(chan error, 1) - kubescoreMetricsChan := make(chan error, 1) - trivyImagescanChan := make(chan error, 1) - trivySbomcanChan := make(chan error, 1) - RakeesErrChan := make(chan error, 1) - // Start a goroutine to handle errors - doneChan := make(chan bool) - go func() { - // for loop will wait for the error channels - // logs if any error occurs - for { - select { - case err := <-outdatedErrChan: - if err != nil { - log.Println(err) - } - case err := <-kubePreUpgradeChan: - if err != nil { - log.Println(err) - } - case err := <-getAllResourceChan: - if err != nil { - log.Println(err) - } - case err := <-clusterMetricsChan: - if err != nil { - log.Println(err) - } - case err := <-kubescoreMetricsChan: - if err != nil { - log.Println(err) - } - case err := <-trivyImagescanChan: - if err != nil { - log.Println(err) - } - case err := <-trivySbomcanChan: - if err != nil { - log.Println(err) - } - case err := <-trivyK8sMetricsChan: - if err != nil { - log.Println(err) - } - case err := <-RakeesErrChan: - if err != nil { - log.Println(err) - } - case <-doneChan: - return // All other goroutines have finished, so exit the goroutine - } - } - }() - wg.Add(7) // Initialize the WaitGroup for the seven goroutines - // ... start other goroutines ... - go outDatedImages(config, js, &wg, outdatedErrChan) - go KubePreUpgradeDetector(config, js, &wg, kubePreUpgradeChan) - go GetAllResources(config, js, &wg, getAllResourceChan) - go RakeesOutput(config, js, &wg, RakeesErrChan) - go getK8sEvents(clientset) - // Run these functions sequentially within a single goroutine using the wrapper function - go runTrivyScans(config, js, &wg, trivyImagescanChan, trivySbomcanChan, trivyK8sMetricsChan) - go RunKubeScore(clientset, js, &wg, kubescoreMetricsChan) - wg.Wait() - // once the go routines completes we will close the error channels - close(outdatedErrChan) - close(kubePreUpgradeChan) - close(getAllResourceChan) - // close(clusterMetricsChan) - close(kubescoreMetricsChan) - close(trivyImagescanChan) - close(trivySbomcanChan) - close(trivyK8sMetricsChan) - close(RakeesErrChan) - // Signal that all other goroutines have finished - doneChan <- true - close(doneChan) + err := outDatedImages(config, js) + LogErr(err) + err = KubePreUpgradeDetector(config, js) + LogErr(err) + err = GetAllResources(config, js) + LogErr(err) + err = RakeesOutput(config, js) + LogErr(err) + getK8sEvents(clientset) + err = runTrivyScans(config, js) + LogErr(err) + err = RunKubeScore(clientset, js) + LogErr(err) } + collectAndPublishMetrics() if schedulingIntervalStr == "" { schedulingIntervalStr = "20m" // Default value, e.g., 20 minutes @@ -201,7 +141,6 @@ func main() { // with subject "METRICS.created" func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { watchK8sEvents(clientset, js) - errCh <- nil } @@ -294,7 +233,11 @@ func checkErr(err error) { log.Fatal(err) } } - +func LogErr(err error) { + if err != nil { + log.Println(err) + } +} func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { watchlist := cache.NewListWatchFromClient( clientset.CoreV1().RESTClient(), diff --git a/agent/kubviz/ketall.go b/agent/kubviz/ketall.go index f1423564..ce6d125f 100644 --- a/agent/kubviz/ketall.go +++ b/agent/kubviz/ketall.go @@ -3,10 +3,10 @@ package main import ( "context" "encoding/json" - "github.com/intelops/kubviz/constants" - "sync" "time" + "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" log "github.com/sirupsen/logrus" @@ -28,8 +28,7 @@ func PublishAllResources(result model.Resource, js nats.JetStreamContext) error return nil } -func GetAllResources(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGroup, errCh chan error) { - defer wg.Done() +func GetAllResources(config *rest.Config, js nats.JetStreamContext) error { // TODO: upto this uncomment for production // Create a new discovery client to discover all resources in the cluster dc := discovery.NewDiscoveryClientForConfigOrDie(config) @@ -37,19 +36,16 @@ func GetAllResources(config *rest.Config, js nats.JetStreamContext, wg *sync.Wai // Create a new dynamic client to list resources in the cluster dynamicClient, err := dynamic.NewForConfig(config) if err != nil { - log.Error(err) - errCh <- err + return err } // Get a list of all available API groups and versions in the cluster resourceLists, err := dc.ServerPreferredResources() if err != nil { - log.Error(err) - errCh <- err + return err } gvrs, err := discovery.GroupVersionResources(resourceLists) if err != nil { - panic(err) - errCh <- err + return err } // Iterate over all available API groups and versions and list all resources in each group for gvr := range gvrs { @@ -81,9 +77,9 @@ func GetAllResources(config *rest.Config, js nats.JetStreamContext, wg *sync.Wai } err := PublishAllResources(resource, js) if err != nil { - errCh <- err + return err } } } - errCh <- nil + return nil } diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index 9c993a59..2c49997e 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -4,12 +4,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/intelops/kubviz/constants" "io" "net/http" "os" "strings" - "sync" + + "github.com/intelops/kubviz/constants" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -79,29 +79,26 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon return nil } -func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGroup, errCh chan error) { - defer wg.Done() +func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error { swaggerdir, err := os.MkdirTemp("", "kubepug") if err != nil { - errCh <- err + return err } filename := fmt.Sprintf("%s/swagger-%s.json", swaggerdir, k8sVersion) url := fmt.Sprintf("%s/%s/%s", baseURL, k8sVersion, fileURL) err = downloadFile(filename, url) if err != nil { - errCh <- err + return err } defer os.RemoveAll(filename) swaggerfile := filename kubernetesAPIs, err := PopulateKubeAPIMap(swaggerfile) if err != nil { - errCh <- err + return err } result = getResults(config, kubernetesAPIs) err = publishK8sDepricated_Deleted_Api(result, js) - errCh <- err - // b, _ := json.MarshalIndent(result, "", " ") - // fmt.Printf("%s", string(b)) + return err } func PopulateKubeAPIMap(swagfile string) (model.KubernetesAPIs, error) { diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index f7a2b838..437a0850 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -3,48 +3,47 @@ package main import ( "context" "encoding/json" + "log" + exec "os/exec" + "github.com/google/uuid" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - "log" - exec "os/exec" - "sync" ) -func RunKubeScore(clientset *kubernetes.Clientset, js nats.JetStreamContext, wg *sync.WaitGroup, errCh chan error) { - defer wg.Done() - +func RunKubeScore(clientset *kubernetes.Clientset, js nats.JetStreamContext) error { nsList, err := clientset.CoreV1(). Namespaces(). List(context.Background(), metav1.ListOptions{}) if err != nil { log.Println("Error occurred while getting client set for kube-score: ", err) - return + return err } log.Printf("Namespace size: %d", len(nsList.Items)) for _, n := range nsList.Items { log.Printf("Publishing kube-score recommendations for namespace: %s\n", n.Name) - publish(n.Name, js, errCh) + publish(n.Name, js) } + return nil } -func publish(ns string, js nats.JetStreamContext, errCh chan error) { +func publish(ns string, js nats.JetStreamContext) error { cmd := "kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c \"kubectl get {} -n " + ns + " -oyaml && echo ---\" | kube-score score - " log.Printf("Command: %#v,", cmd) out, err := executeCommand(cmd) if err != nil { log.Println("Error occurred while running kube-score: ", err) - errCh <- err + return err } err = publishKubescoreMetrics(uuid.New().String(), ns, out, js) if err != nil { - errCh <- err + return err } - errCh <- nil + return nil } func publishKubescoreMetrics(id string, ns string, recommendations string, js nats.JetStreamContext) error { diff --git a/agent/kubviz/outdated.go b/agent/kubviz/outdated.go index 68ec985e..4470abe8 100644 --- a/agent/kubviz/outdated.go +++ b/agent/kubviz/outdated.go @@ -66,12 +66,11 @@ func PublishOutdatedImages(out model.CheckResultfinal, js nats.JetStreamContext) return nil } -func outDatedImages(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGroup, errCh chan error) { - defer wg.Done() +func outDatedImages(config *rest.Config, js nats.JetStreamContext) error { images, err := ListImages(config) if err != nil { log.Println("unable to list images") - errCh <- err + return err } for _, image := range images { namespace := image.Namespace @@ -92,7 +91,7 @@ func outDatedImages(config *rest.Config, js nats.JetStreamContext, wg *sync.Wait final.Pod = pod err := PublishOutdatedImages(final, js) if err != nil { - errCh <- err + return err } } else { if checkResult != nil { @@ -108,7 +107,7 @@ func outDatedImages(config *rest.Config, js nats.JetStreamContext, wg *sync.Wait final.Pod = pod err := PublishOutdatedImages(final, js) if err != nil { - errCh <- err + return err } } else { tagtrunk := truncateTagName(tag) @@ -125,12 +124,13 @@ func outDatedImages(config *rest.Config, js nats.JetStreamContext, wg *sync.Wait final.Pod = pod err := PublishOutdatedImages(final, js) if err != nil { - errCh <- err + return err } } } } } + return nil } func ParseImageName(imageName string) (string, string, string, error) { diff --git a/agent/kubviz/rakees_agent.go b/agent/kubviz/rakees_agent.go index 54e610fb..0b8426c1 100644 --- a/agent/kubviz/rakees_agent.go +++ b/agent/kubviz/rakees_agent.go @@ -4,13 +4,13 @@ import ( "context" "encoding/json" "fmt" - "github.com/intelops/kubviz/constants" "log" "os" "os/signal" - "sync" "syscall" + "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/agent/kubviz/rakkess" "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" @@ -33,17 +33,17 @@ func accessToOutcome(access rakkess.Access) (rakkess.Outcome, error) { } } -func RakeesOutput(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGroup, errCh chan error) { +func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { // Create a new Kubernetes client client, err := kubernetes.NewForConfig(config) if err != nil { - errCh <- err + return err } // Retrieve all available resource types resourceList, err := client.Discovery().ServerPreferredResources() if err != nil { - errCh <- err + return err } var opts = rakkess.NewRakkessOptions() opts.Verbs = []string{"list", "create", "update", "delete"} @@ -56,25 +56,25 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGr res, err := rakkess.Resource(ctx, opts) if err != nil { fmt.Println("Error") - errCh <- err + return err } fmt.Println("Result..") for resourceType, access := range res { createOutcome, err := accessToOutcome(access["create"]) if err != nil { - errCh <- err + return err } deleteOutcome, err := accessToOutcome(access["delete"]) if err != nil { - errCh <- err + return err } listOutcome, err := accessToOutcome(access["list"]) if err != nil { - errCh <- err + return err } updateOutcome, err := accessToOutcome(access["update"]) if err != nil { - errCh <- err + return err } metrics := model.RakeesMetrics{ ClusterName: ClusterName, @@ -87,12 +87,11 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext, wg *sync.WaitGr metricsJson, _ := json.Marshal(metrics) _, err = js.Publish(constants.EventSubject_rakees, metricsJson) if err != nil { - errCh <- err + return err } log.Printf("Metrics with resource %s has been published", resourceType) } - // t := res.Table(opts.Verbs) - // t.Render(opts.Streams.Out, opts.OutputFormat) + return nil } diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 5951e54f..654cf481 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -12,15 +12,14 @@ import ( "github.com/nats-io/nats.go" ) -func RunTrivyK8sClusterScan(js nats.JetStreamContext, errCh chan error) { +func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport out, err := executeCommand("trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") log.Println("Commnd for k8s cluster scan: trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") parts := strings.SplitN(out, "{", 2) if len(parts) <= 1 { log.Println("No output from k8s cluster scan command", err) - errCh <- err - return + return err } log.Println("Command logs for k8s cluster scan", parts[0]) jsonPart := "{" + parts[1] @@ -29,12 +28,16 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext, errCh chan error) { err = json.Unmarshal([]byte(jsonPart), &report) if err != nil { log.Printf("Error occurred while Unmarshalling json for k8s cluster scan: %v", err) - errCh <- err + return err } - publishTrivyK8sReport(report, js, errCh) + err = publishTrivyK8sReport(report, js) + if err != nil { + return err + } + return nil } -func publishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamContext, errCh chan error) { +func publishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamContext) error { metrics := model.Trivy{ ID: uuid.New().String(), ClusterName: ClusterName, @@ -43,8 +46,8 @@ func publishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamCo metricsJson, _ := json.Marshal(metrics) _, err := js.Publish(constants.TRIVY_K8S_SUBJECT, metricsJson) if err != nil { - errCh <- err + return err } log.Printf("Trivy k8s cluster report with ID:%s has been published\n", metrics.ID) - errCh <- nil + return nil } diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index 6e2af2fe..4eaac417 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -13,7 +13,7 @@ import ( "k8s.io/client-go/rest" ) -func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext, errCh chan error) { +func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { images, err := ListImages(config) if err != nil { log.Fatal(err) @@ -43,13 +43,15 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext, errCh cha log.Printf("Error occurred while Unmarshalling json for image: %v", err) continue // Move on to the next image in case of an error } - publishImageScanReports(report, js, errCh) - // If you want to publish the report or perform any other action with it, you can do it here - + err = publishImageScanReports(report, js) + if err != nil { + return err + } } + return nil } -func publishImageScanReports(report types.Report, js nats.JetStreamContext, errCh chan error) { +func publishImageScanReports(report types.Report, js nats.JetStreamContext) error { metrics := model.TrivyImage{ ID: uuid.New().String(), ClusterName: ClusterName, @@ -58,8 +60,8 @@ func publishImageScanReports(report types.Report, js nats.JetStreamContext, errC metricsJson, _ := json.Marshal(metrics) _, err := js.Publish(constants.TRIVY_IMAGE_SUBJECT, metricsJson) if err != nil { - errCh <- err + return err } log.Printf("Trivy image report with ID:%s has been published\n", metrics.ID) - errCh <- nil + return nil } diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 34ca7712..d33d2709 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -14,7 +14,7 @@ import ( "k8s.io/client-go/rest" ) -func publishTrivySbomReport(report model.Sbom, js nats.JetStreamContext, errCh chan error) { +func publishTrivySbomReport(report model.Sbom, js nats.JetStreamContext) error { metrics := model.Reports{ ID: uuid.New().String(), Report: report, @@ -22,11 +22,11 @@ func publishTrivySbomReport(report model.Sbom, js nats.JetStreamContext, errCh c metricsJson, _ := json.Marshal(metrics) _, err := js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) if err != nil { - errCh <- err + return err } log.Printf("Trivy report with BomFormat:%v has been published\n", metrics.Report.BomFormat) - errCh <- nil + return nil } func executeCommandSbom(command string) ([]byte, error) { @@ -44,7 +44,7 @@ func executeCommandSbom(command string) ([]byte, error) { return outc.Bytes(), err } -func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext, errCh chan error) { +func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Println("trivy sbom run started") images, err := ListImages(config) @@ -77,6 +77,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext, errCh chan log.Println("report", report) // Publish the report using the given function - publishTrivySbomReport(report, js, errCh) + publishTrivySbomReport(report, js) } + return nil } From d5958c7563f982577b447f8d984b755245fe5824 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 5 Sep 2023 11:08:04 +0530 Subject: [PATCH 006/263] quay-working --- agent/container/api/agent.gen.go | 30 ++++++++++---- agent/container/cfg.yaml | 3 +- agent/container/openapi.yaml | 8 ++++ agent/container/pkg/handler/api_handler.go | 1 + agent/container/pkg/handler/quay_handler.go | 40 ++++++++++++++++++ client/pkg/clickhouse/db_client.go | 45 ++++++++++++++++++++- client/pkg/clickhouse/statements.go | 12 ++++++ client/pkg/clients/container_client.go | 10 +++++ model/quay.go | 10 +++++ 9 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 agent/container/pkg/handler/quay_handler.go create mode 100644 model/quay.go diff --git a/agent/container/api/agent.gen.go b/agent/container/api/agent.gen.go index 3b9beab7..81131417 100644 --- a/agent/container/api/agent.gen.go +++ b/agent/container/api/agent.gen.go @@ -27,6 +27,9 @@ type ServerInterface interface { // Post Dockerhub artifactory events // (POST /event/docker/hub) PostEventDockerHub(c *gin.Context) + // Post quay Container Registry webhook events + // (POST /event/quay/container) + PostEventQuayContainer(c *gin.Context) // Kubernetes readiness and liveness probe endpoint // (GET /status) GetStatus(c *gin.Context) @@ -71,6 +74,16 @@ func (siw *ServerInterfaceWrapper) PostEventDockerHub(c *gin.Context) { siw.Handler.PostEventDockerHub(c) } +// PostEventQuayContainer operation middleware +func (siw *ServerInterfaceWrapper) PostEventQuayContainer(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + } + + siw.Handler.PostEventQuayContainer(c) +} + // GetStatus operation middleware func (siw *ServerInterfaceWrapper) GetStatus(c *gin.Context) { @@ -116,6 +129,8 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options router.POST(options.BaseURL+"/event/docker/hub", wrapper.PostEventDockerHub) + router.POST(options.BaseURL+"/event/quay/container", wrapper.PostEventQuayContainer) + router.GET(options.BaseURL+"/status", wrapper.GetStatus) return router @@ -124,13 +139,14 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/6SSQWscMQyF/4rQebqzaW9zC01oQwoJ2d5CDh5buyMyaxtJnrJd5r8Xz9K0pCUJ7ckI", - "3nv6JOuIPu1zihRNsTvODXLcJuyOGEi9cDZOETv8mKI5jiTQC4cdwU2mCHeXm69wfnsFmsnzlr1b5A0a", - "20iv2zbPbBOJnvqdrdarNc4NpkzRZcYOP6zWqzNsMDsbKiu2LvO7kPxS7MjqkzLJknYVsMNPZOeZL6qk", - "QSHNKSot8vfr9Z9D3lzjPDeoZb93csAOv7AapG1lVciSJg4UoD+ADQRKMrGnOq3bKXb3mEs/sseHGtLS", - "RNFa970Itf7nGmrPnPQvqLdJ7bJazqvjaW//xl3DYAmCXz9wRztWkwN8o35I6REWQn2ZPyT/SNIOpX8D", - "+sUi/lz6/6A+ZQylByfGW+ctyeEVVDVn5cUr2JwUb8HS4j2pbssITzHPQK9LTxLJSEHIBY6kCi4GGHmi", - "pciSegKKISeO9ju38OSMKniNJKknj939EYuM2GGL88P8IwAA//90yrlhlQMAAA==", + "H4sIAAAAAAAC/6yTQW/TQBCF/8poziZO4eZbRCuoitTScKt6WK8nyajO7jIza2Qi/3e0jiiooBIKJ2uk", + "995882wf0Md9ioGCKTaHqUIOm4jNATtSL5yMY8AG38ZgjgMJtMLdluA6UYDbi/UnWN1cgibyvGHvZnmF", + "xtbTn23rJ7aBRI/7zhbLxRKnCmOi4BJjg28Wy8UZVpic7Qor1i7xqy76ediSlUdMJHPaZYcNviNbJT4v", + "kgqFNMWgNMtfL5e/Hnl9hdNUoeb93smIDX5gNYibwqqQJA7cUQftCLYjUJKBPZVr3VaxucOU25493peQ", + "mgYKVruvWaj232soO1PU36DeRLWLYlkVx2NvL+MuYTAHwY83cEtbVpMRvlC7i/EBZkJ9nr+L/oGk3uX2", + "BPTzWfw+t/9AfczY5RacGG+ctyjjSaifsxv/qumP2Y3/o+iy+OU9qznLz37C66PiFEDN3pPqJvfwGPME", + "+Sq3JIGMFIRcx4FUwYUOeh5oHpLEloBClyIH+5lbeHBGBbxEkpT/FZu7A2bpscEap/vpWwAAAP//KFB7", + "SlIEAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/agent/container/cfg.yaml b/agent/container/cfg.yaml index 845d5fb1..59622b8b 100644 --- a/agent/container/cfg.yaml +++ b/agent/container/cfg.yaml @@ -3,4 +3,5 @@ generate: gin-server: true models: true embedded-spec: true -output: agent/container/api/agent.gen.go +output: api/agent.gen.go + diff --git a/agent/container/openapi.yaml b/agent/container/openapi.yaml index bcd76a59..3c0dba28 100755 --- a/agent/container/openapi.yaml +++ b/agent/container/openapi.yaml @@ -44,5 +44,13 @@ paths: responses: '200': description: OK + /event/quay/container: + post: + tags: + - public + summary: Post quay Container Registry webhook events + responses: + '200': + description: OK # oapi-codegen -config ./cfg.yaml ./openapi.yaml diff --git a/agent/container/pkg/handler/api_handler.go b/agent/container/pkg/handler/api_handler.go index 5e5e2834..0408926d 100755 --- a/agent/container/pkg/handler/api_handler.go +++ b/agent/container/pkg/handler/api_handler.go @@ -35,6 +35,7 @@ func (ah *APIHandler) BindRequest(r *gin.Engine) { apiGroup.GET("/status", ah.GetStatus) apiGroup.POST("/event/docker/hub", ah.PostEventDockerHub) apiGroup.POST("/event/azure/container", ah.PostEventAzureContainer) + apiGroup.POST("/event/quay/container", ah.PostEventQuayContainer) } } diff --git a/agent/container/pkg/handler/quay_handler.go b/agent/container/pkg/handler/quay_handler.go new file mode 100644 index 00000000..a002e05d --- /dev/null +++ b/agent/container/pkg/handler/quay_handler.go @@ -0,0 +1,40 @@ +package handler + +import ( + "encoding/json" + "io" + "log" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/intelops/kubviz/model" +) + +func (ah *APIHandler) PostEventQuayContainer(c *gin.Context) { + defer func() { + _, _ = io.Copy(io.Discard, c.Request.Body) + _ = c.Request.Body.Close() + }() + payload, err := io.ReadAll(c.Request.Body) + if err != nil || len(payload) == 0 { + log.Printf("%v: %v", ErrReadingBody, err) + c.Status(http.StatusBadRequest) + return + } + var pushEvent model.QuayImagePushPayload + err = json.Unmarshal(payload, &pushEvent) + if err != nil { + log.Printf("%v: %v", ErrInvalidPayload, err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Bad Request"}) + return + } + log.Printf("Received event from Quay Container Registry: %v", pushEvent) + + err = ah.conn.Publish(payload, "Quay_Container_Registry") + if err != nil { + log.Printf("%v: %v", ErrPublishToNats, err) + c.Status(http.StatusInternalServerError) + return + } + c.Status(http.StatusOK) +} \ No newline at end of file diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 7ef2c634..470f2514 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -41,6 +41,7 @@ type DBInterface interface { RetrieveKubvizEvent() ([]model.DbEvent, error) InsertContainerEventDockerHub(model.DockerHubBuild) InsertContainerEventAzure(model.AzureContainerPushEventPayload) + InsertContainerEventQuay(model.QuayImagePushPayload) InsertContainerEventGithub(string) InsertGitCommon(metrics model.GitCommonAttribute, statement dbstatement.DBStatement) error Close() @@ -72,7 +73,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { return nil, err } - tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerDockerhubTable, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} + tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerDockerhubTable, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable,quayContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} for _, table := range tables { if err = splconn.Exec(context.Background(), string(table)); err != nil { return nil, err @@ -131,6 +132,48 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE log.Fatal(err) } } +func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { + var ( + tx, _ = c.conn.Begin() + stmt, _ = tx.Prepare(string(InsertQuayContainerPushEvent)) + ) + defer stmt.Close() + dockerURL := pushEvent.DockerURL + repository := pushEvent.Repository + //tag := pushEvent.UpdatedTags + name := pushEvent.Name + nameSpace := pushEvent.Namespace + homePage := pushEvent.Homepage + + var tag string + if pushEvent.UpdatedTags != nil { + tag = strings.Join(pushEvent.UpdatedTags, ",") + } else { + tag = "" + } + + // Marshaling the pushEvent into a JSON string + pushEventJSON, err := json.Marshal(pushEvent) + if err != nil { + log.Printf("Error while marshaling Quay Container Registry payload: %v", err) + return + } + + if _, err := stmt.Exec( + name, + repository, + nameSpace, + dockerURL, + homePage, + tag, + string(pushEventJSON), + ); err != nil { + log.Fatal(err) + } + if err := tx.Commit(); err != nil { + log.Fatal(err) + } +} func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { var ( diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 7646af14..27e2c50d 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -157,6 +157,17 @@ const azureContainerPushEventTable DBStatement = ` SHAID String ) engine=File(TabSeparated) ` +const quayContainerPushEventTable DBStatement = ` + CREATE TABLE IF NOT EXISTS quaycontainerpush ( + name String, + repository String, + nameSpace String, + dockerURL String, + homePage String, + tag String, + Event String + ) engine=File(TabSeparated) + ` const trivySbomTable DBStatement = ` CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, @@ -199,3 +210,4 @@ const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artif const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Timestamp, Size, SHAID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivySbom string = "INSERT INTO trivysbom (id, schema, bom_format,spec_version,serial_number, version, metadata_timestamp,metatool_vendor,metatool_name,metatool_version,component_bom_ref,component_type,component_name,component_version,component_property_name,component_property_value,component_hash_alg,component_hash_content,component_license_exp,component_purl,dependency_ref) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" +const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage,tag, Event) VALUES (?, ?, ?, ?, ?, ?, ?)" diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index 3688a102..e7f60f5e 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -59,6 +59,16 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { // Extract the necessary information from pushEvent and insert into ClickHouse conn.InsertContainerEventAzure(pushEvent) log.Println("Inserted Azure Container Registry metrics:", string(msg.Data)) + }else if repoName == "Quay_Container_Registry" { + var pushEvent model.QuayImagePushPayload + err := json.Unmarshal(msg.Data, &pushEvent) + if err != nil { + log.Printf("Error while unmarshaling Quay Container Registry payload: %v", err) + return + } + // Extract the necessary information from pushEvent and insert into ClickHouse + conn.InsertContainerEventQuay(pushEvent) + log.Println("Inserted Quay Container Registry metrics:", string(msg.Data)) } }, nats.Durable(string(containerConsumer)), nats.ManualAck()) diff --git a/model/quay.go b/model/quay.go new file mode 100644 index 00000000..f8ae495b --- /dev/null +++ b/model/quay.go @@ -0,0 +1,10 @@ +package model + +type QuayImagePushPayload struct { + Name string `json:"name"` + Repository string `json:"repository"` + Namespace string `json:"namespace"` + DockerURL string `json:"docker_url"` + Homepage string `json:"homepage"` + UpdatedTags []string `json:"updated_tags"` +} From 5e3f3d2ca1b40db2f96450225aa066d590cdbb8b Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 5 Sep 2023 14:19:41 +0530 Subject: [PATCH 007/263] testing --- agent/container/api/agent.gen.go | 31 +++++++++---- agent/container/openapi.yaml | 8 ++++ agent/container/pkg/handler/api_handler.go | 1 + .../container/pkg/handler/jfrog_container.go | 45 +++++++++++++++++++ client/pkg/clickhouse/db_client.go | 43 +++++++++++++++++- client/pkg/clickhouse/statements.go | 14 ++++++ client/pkg/clients/container_client.go | 12 ++++- go.mod | 1 - go.sum | 2 - model/jfrogcontainer.go | 19 ++++++++ 10 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 agent/container/pkg/handler/jfrog_container.go create mode 100644 model/jfrogcontainer.go diff --git a/agent/container/api/agent.gen.go b/agent/container/api/agent.gen.go index 81131417..477b57a9 100644 --- a/agent/container/api/agent.gen.go +++ b/agent/container/api/agent.gen.go @@ -27,6 +27,9 @@ type ServerInterface interface { // Post Dockerhub artifactory events // (POST /event/docker/hub) PostEventDockerHub(c *gin.Context) + // Post Jfrog Container Registry webhook events + // (POST /event/jfrog/container) + PostEventJfrogContainer(c *gin.Context) // Post quay Container Registry webhook events // (POST /event/quay/container) PostEventQuayContainer(c *gin.Context) @@ -74,6 +77,16 @@ func (siw *ServerInterfaceWrapper) PostEventDockerHub(c *gin.Context) { siw.Handler.PostEventDockerHub(c) } +// PostEventJfrogContainer operation middleware +func (siw *ServerInterfaceWrapper) PostEventJfrogContainer(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + } + + siw.Handler.PostEventJfrogContainer(c) +} + // PostEventQuayContainer operation middleware func (siw *ServerInterfaceWrapper) PostEventQuayContainer(c *gin.Context) { @@ -129,6 +142,8 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options router.POST(options.BaseURL+"/event/docker/hub", wrapper.PostEventDockerHub) + router.POST(options.BaseURL+"/event/jfrog/container", wrapper.PostEventJfrogContainer) + router.POST(options.BaseURL+"/event/quay/container", wrapper.PostEventQuayContainer) router.GET(options.BaseURL+"/status", wrapper.GetStatus) @@ -139,14 +154,14 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/6yTQW/TQBCF/8poziZO4eZbRCuoitTScKt6WK8nyajO7jIza2Qi/3e0jiiooBIKJ2uk", - "995882wf0Md9ioGCKTaHqUIOm4jNATtSL5yMY8AG38ZgjgMJtMLdluA6UYDbi/UnWN1cgibyvGHvZnmF", - "xtbTn23rJ7aBRI/7zhbLxRKnCmOi4BJjg28Wy8UZVpic7Qor1i7xqy76ediSlUdMJHPaZYcNviNbJT4v", - "kgqFNMWgNMtfL5e/Hnl9hdNUoeb93smIDX5gNYibwqqQJA7cUQftCLYjUJKBPZVr3VaxucOU25493peQ", - "mgYKVruvWaj232soO1PU36DeRLWLYlkVx2NvL+MuYTAHwY83cEtbVpMRvlC7i/EBZkJ9nr+L/oGk3uX2", - "BPTzWfw+t/9AfczY5RacGG+ctyjjSaifsxv/qumP2Y3/o+iy+OU9qznLz37C66PiFEDN3pPqJvfwGPME", - "+Sq3JIGMFIRcx4FUwYUOeh5oHpLEloBClyIH+5lbeHBGBbxEkpT/FZu7A2bpscEap/vpWwAAAP//KFB7", - "SlIEAAA=", + "H4sIAAAAAAAC/6yUz2/TThDF/5XVnP2N0y833yJaQSlSS8Ot6mG9O06GOrvLzKyRify/o3VEQQWV9MfJ", + "Gum9N595lr0HF3cpBgwq0OynCih0EZo9eBTHlJRigAbexqCWArJpmfwGzWXCYK7P1p/N6urcSEJHHTk7", + "yytQ0h7/bVs/sA3Icth3slguljBVEBMGmwgaeLNYLk6ggmR1W1ihton+89HNwwa1PGJCntPOPTTwDnWV", + "6LRIKmCUFIPgLP9/ufzzyMsLmKYKJO92lkdo4COJmtgVVjGJ40AevWlHo1s0gjyQw3Kt3Qg0N5By25OD", + "2xJS44BBa/s9M9buZw1lZ4ryF9SrKHpWLKviuO/tedwlzMxB5tcbuMYNifJovmG7jfHOzITyOL+P7g65", + "3ub2CPTTWfw+ty+gPmRsc2ssK3XWaeTxKNQvHcfNk6r+UByvUfUc9NKqv2Y7Pgn/U7bja9CXxc+HF7Wa", + "H/0E1wfFMYCSnUORLvfmPuYB8kVukQMqimG0ngKKGBu86WnAeUgcWzQYfIoU9HdupsEqFvASiVz+N9Dc", + "7CFzDw3UMN1OPwIAAP//BN6tkhIFAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/agent/container/openapi.yaml b/agent/container/openapi.yaml index 3c0dba28..de7b8eb2 100755 --- a/agent/container/openapi.yaml +++ b/agent/container/openapi.yaml @@ -52,5 +52,13 @@ paths: responses: '200': description: OK + /event/jfrog/container: + post: + tags: + - public + summary: Post Jfrog Container Registry webhook events + responses: + '200': + description: OK # oapi-codegen -config ./cfg.yaml ./openapi.yaml diff --git a/agent/container/pkg/handler/api_handler.go b/agent/container/pkg/handler/api_handler.go index 0408926d..531fd461 100755 --- a/agent/container/pkg/handler/api_handler.go +++ b/agent/container/pkg/handler/api_handler.go @@ -36,6 +36,7 @@ func (ah *APIHandler) BindRequest(r *gin.Engine) { apiGroup.POST("/event/docker/hub", ah.PostEventDockerHub) apiGroup.POST("/event/azure/container", ah.PostEventAzureContainer) apiGroup.POST("/event/quay/container", ah.PostEventQuayContainer) + apiGroup.POST("/event/jfrog/container", ah.PostEventJfrogContainer) } } diff --git a/agent/container/pkg/handler/jfrog_container.go b/agent/container/pkg/handler/jfrog_container.go new file mode 100644 index 00000000..5655026c --- /dev/null +++ b/agent/container/pkg/handler/jfrog_container.go @@ -0,0 +1,45 @@ +package handler + +import ( + "encoding/json" + "errors" + "io" + "log" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/intelops/kubviz/model" +) + +var ErrInvalidPayloads = errors.New("invalid or malformed jfrog Container Registry webhook payload") + +func (ah *APIHandler) PostEventJfrogContainer(c *gin.Context) { + defer func() { + _, _ = io.Copy(io.Discard, c.Request.Body) + _ = c.Request.Body.Close() + }() + payload, err := io.ReadAll(c.Request.Body) + if err != nil || len(payload) == 0 { + log.Printf("%v: %v", ErrReadingBody, err) + c.Status(http.StatusBadRequest) + return + } + + var pushEvent model.JfrogContainerPushEventPayload + err = json.Unmarshal(payload, &pushEvent) + if err != nil { + log.Printf("%v: %v", ErrInvalidPayloads, err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Bad Request"}) + return + } + + log.Printf("Received event from jfrog Container Registry: %v", pushEvent) + + err = ah.conn.Publish(payload, "Jfrog_Container_Registry") + if err != nil { + log.Printf("%v: %v", ErrPublishToNats, err) + c.Status(http.StatusInternalServerError) + return + } + c.Status(http.StatusOK) +} diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 470f2514..308ad43a 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -42,6 +42,7 @@ type DBInterface interface { InsertContainerEventDockerHub(model.DockerHubBuild) InsertContainerEventAzure(model.AzureContainerPushEventPayload) InsertContainerEventQuay(model.QuayImagePushPayload) + InsertContainerEventJfrog(model.JfrogContainerPushEventPayload) InsertContainerEventGithub(string) InsertGitCommon(metrics model.GitCommonAttribute, statement dbstatement.DBStatement) error Close() @@ -73,7 +74,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { return nil, err } - tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerDockerhubTable, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable,quayContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} + tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerDockerhubTable, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, quayContainerPushEventTable, jfrogContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} for _, table := range tables { if err = splconn.Exec(context.Background(), string(table)); err != nil { return nil, err @@ -174,7 +175,47 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload log.Fatal(err) } } +func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { + var ( + tx, _ = c.conn.Begin() + stmt, _ = tx.Prepare(string(InsertJfrogContainerPushEvent)) + ) + defer stmt.Close() + registryURL := pushEvent.Data.Path + repositoryName := pushEvent.Data.Name + tag := pushEvent.Data.Tag + + if tag == "" { + tag = "latest" + } + imageName := pushEvent.Data.ImageName + size := pushEvent.Data.Size + shaID := pushEvent.Data.SHA256 + + // Marshaling the pushEvent into a JSON string + pushEventJSON, err := json.Marshal(pushEvent) + if err != nil { + log.Printf("Error while marshaling Azure Container Registry payload: %v", err) + return + } + if _, err := stmt.Exec( + pushEvent.Domain, + pushEvent.EventType, + registryURL, + repositoryName, + shaID, + size, + imageName, + tag, + string(pushEventJSON), + ); err != nil { + log.Fatal(err) + } + if err := tx.Commit(); err != nil { + log.Fatal(err) + } +} func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { var ( tx, _ = c.conn.Begin() diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 27e2c50d..6e1c900f 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -51,6 +51,19 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( Scope String ) engine=File(TabSeparated) ` +const jfrogContainerPushEventTable DBStatement = ` + CREATE TABLE IF NOT EXISTS azurecontainerpush ( + Domain String, + EventType String, + RegistryURL String, + RepositoryName String, + SHAID String, + Size Int32, + ImageName String, + Tag String, + Event String + ) engine=File(TabSeparated) + ` const ketallTable DBStatement = ` CREATE TABLE IF NOT EXISTS getall_resources ( ClusterName String, @@ -211,3 +224,4 @@ const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_na const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Timestamp, Size, SHAID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivySbom string = "INSERT INTO trivysbom (id, schema, bom_format,spec_version,serial_number, version, metadata_timestamp,metatool_vendor,metatool_name,metatool_version,component_bom_ref,component_type,component_name,component_version,component_property_name,component_property_value,component_hash_alg,component_hash_content,component_license_exp,component_purl,dependency_ref) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage,tag, Event) VALUES (?, ?, ?, ?, ?, ?, ?)" +const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType,RegistryURL, RepositoryName,SHAID, Size, ImageName ,Tag, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?)" diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index e7f60f5e..dbd17c9b 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -59,7 +59,7 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { // Extract the necessary information from pushEvent and insert into ClickHouse conn.InsertContainerEventAzure(pushEvent) log.Println("Inserted Azure Container Registry metrics:", string(msg.Data)) - }else if repoName == "Quay_Container_Registry" { + } else if repoName == "Quay_Container_Registry" { var pushEvent model.QuayImagePushPayload err := json.Unmarshal(msg.Data, &pushEvent) if err != nil { @@ -69,6 +69,16 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { // Extract the necessary information from pushEvent and insert into ClickHouse conn.InsertContainerEventQuay(pushEvent) log.Println("Inserted Quay Container Registry metrics:", string(msg.Data)) + } else if repoName == "Jfrog_Container_Registry" { + var pushEvent model.JfrogContainerPushEventPayload + err := json.Unmarshal(msg.Data, &pushEvent) + if err != nil { + log.Printf("Error while unmarshaling Azure Container Registry payload: %v", err) + return + } + // Extract the necessary information from pushEvent and insert into ClickHouse + conn.InsertContainerEventJfrog(pushEvent) + log.Println("Inserted Azure Container Registry metrics:", string(msg.Data)) } }, nats.Durable(string(containerConsumer)), nats.ManualAck()) diff --git a/go.mod b/go.mod index 8188081c..f59496e8 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/nats-io/nats.go v1.27.1 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 - github.com/vijeyash1/go-github-container v1.0.0 golang.org/x/term v0.10.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 diff --git a/go.sum b/go.sum index babc6d30..1b905253 100644 --- a/go.sum +++ b/go.sum @@ -440,8 +440,6 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95 github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= -github.com/vijeyash1/go-github-container v1.0.0 h1:SWtzxwGFFSCn8UB27IMcCbQ9xg1l6sQgk3pW2aD0fsQ= -github.com/vijeyash1/go-github-container v1.0.0/go.mod h1:yljHpWvbjXtjy48MXoBonmrTBUYNk8iA0cACfyU0Om4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= diff --git a/model/jfrogcontainer.go b/model/jfrogcontainer.go new file mode 100644 index 00000000..728ad6f5 --- /dev/null +++ b/model/jfrogcontainer.go @@ -0,0 +1,19 @@ +package model + +type JfrogContainerPushEventPayload struct { + Domain string `json:"domain"` + EventType string `json:"event_type"` + Data struct { + RepoKey string `json:"repo_key"` + Path string `json:"path"` + Name string `json:"name"` + SHA256 string `json:"sha256"` + Size int `json:"size"` + ImageName string `json:"image_name"` + Tag string `json:"tag"` + Platforms []interface{} `json:"platforms"` + } `json:"data"` + SubscriptionKey string `json:"subscription_key"` + JPDOrigin string `json:"jpd_origin"` + Source string `json:"source"` +} From 17c31210c0bb993808ac8a4d1d5b69301516b580 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 5 Sep 2023 15:01:44 +0530 Subject: [PATCH 008/263] fix --- client/pkg/clickhouse/statements.go | 2 +- model/jfrogcontainer.go | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 6e1c900f..9a6c300d 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( ) engine=File(TabSeparated) ` const jfrogContainerPushEventTable DBStatement = ` - CREATE TABLE IF NOT EXISTS azurecontainerpush ( + CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( Domain String, EventType String, RegistryURL String, diff --git a/model/jfrogcontainer.go b/model/jfrogcontainer.go index 728ad6f5..9753aa12 100644 --- a/model/jfrogcontainer.go +++ b/model/jfrogcontainer.go @@ -4,14 +4,13 @@ type JfrogContainerPushEventPayload struct { Domain string `json:"domain"` EventType string `json:"event_type"` Data struct { - RepoKey string `json:"repo_key"` - Path string `json:"path"` - Name string `json:"name"` - SHA256 string `json:"sha256"` - Size int `json:"size"` - ImageName string `json:"image_name"` - Tag string `json:"tag"` - Platforms []interface{} `json:"platforms"` + RepoKey string `json:"repo_key"` + Path string `json:"path"` + Name string `json:"name"` + SHA256 string `json:"sha256"` + Size int `json:"size"` + ImageName string `json:"image_name"` + Tag string `json:"tag"` } `json:"data"` SubscriptionKey string `json:"subscription_key"` JPDOrigin string `json:"jpd_origin"` From a249ada66b3ec25fa36b97a34625753fc8dc4dea Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 5 Sep 2023 15:20:51 +0530 Subject: [PATCH 009/263] fix --- model/jfrogcontainer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/jfrogcontainer.go b/model/jfrogcontainer.go index 9753aa12..b6f1d38e 100644 --- a/model/jfrogcontainer.go +++ b/model/jfrogcontainer.go @@ -8,7 +8,7 @@ type JfrogContainerPushEventPayload struct { Path string `json:"path"` Name string `json:"name"` SHA256 string `json:"sha256"` - Size int `json:"size"` + Size int32 `json:"size"` ImageName string `json:"image_name"` Tag string `json:"tag"` } `json:"data"` From 270d7f8e2578eb67f219a1b211be72da1bfdcf0d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 5 Sep 2023 16:04:35 +0530 Subject: [PATCH 010/263] fix --- client/pkg/clickhouse/db_client.go | 2 +- client/pkg/clients/container_client.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 308ad43a..de3fec40 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -195,7 +195,7 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE // Marshaling the pushEvent into a JSON string pushEventJSON, err := json.Marshal(pushEvent) if err != nil { - log.Printf("Error while marshaling Azure Container Registry payload: %v", err) + log.Printf("Error while marshaling Jfrog Container Registry payload: %v", err) return } diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index dbd17c9b..982f497f 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -73,12 +73,12 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { var pushEvent model.JfrogContainerPushEventPayload err := json.Unmarshal(msg.Data, &pushEvent) if err != nil { - log.Printf("Error while unmarshaling Azure Container Registry payload: %v", err) + log.Printf("Error while unmarshaling Jgrog Container Registry payload: %v", err) return } // Extract the necessary information from pushEvent and insert into ClickHouse conn.InsertContainerEventJfrog(pushEvent) - log.Println("Inserted Azure Container Registry metrics:", string(msg.Data)) + log.Println("Inserted Jfrog Container Registry metrics:", string(msg.Data)) } }, nats.Durable(string(containerConsumer)), nats.ManualAck()) From ac016df6ff79ca7815627118cc74f01637e66a8f Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 5 Sep 2023 16:05:19 +0530 Subject: [PATCH 011/263] fix --- client/pkg/clients/container_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index 982f497f..66015abe 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -73,7 +73,7 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { var pushEvent model.JfrogContainerPushEventPayload err := json.Unmarshal(msg.Data, &pushEvent) if err != nil { - log.Printf("Error while unmarshaling Jgrog Container Registry payload: %v", err) + log.Printf("Error while unmarshaling Jfrog Container Registry payload: %v", err) return } // Extract the necessary information from pushEvent and insert into ClickHouse From f34c2e92a1ba9155aeaf2dba3767324e1ca46463 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 5 Sep 2023 16:12:50 +0530 Subject: [PATCH 012/263] Jfrog-container registry --- client/pkg/clients/container_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index 66015abe..29dc5763 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -76,7 +76,7 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { log.Printf("Error while unmarshaling Jfrog Container Registry payload: %v", err) return } - // Extract the necessary information from pushEvent and insert into ClickHouse + // Extract the necessary information from pushEvent and insert into ClickHouse. conn.InsertContainerEventJfrog(pushEvent) log.Println("Inserted Jfrog Container Registry metrics:", string(msg.Data)) } From 322157acebbdbd7bf1d2c7aca5ee7978e3b3f018 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 5 Sep 2023 23:21:40 +0530 Subject: [PATCH 013/263] removed events logs, it helps to see other logs clearly --- agent/kubviz/k8smetrics_agent.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 3a6f8479..67d47923 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -22,7 +22,6 @@ import ( "fmt" - "github.com/ghodss/yaml" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" @@ -117,7 +116,7 @@ func main() { LogErr(err) err = RakeesOutput(config, js) LogErr(err) - getK8sEvents(clientset) + // getK8sEvents(clientset) err = runTrivyScans(config, js) LogErr(err) err = RunKubeScore(clientset, js) @@ -156,7 +155,7 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre if err != nil { return true, err } - log.Printf("Metrics with ID:%s has been published\n", id) + // log.Printf("Metrics with ID:%s has been published\n", id) return false, nil } @@ -252,22 +251,22 @@ func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { event := obj.(*v1.Event) - fmt.Printf("Event namespace: %s \n", event.GetNamespace()) - y, err := yaml.Marshal(event) - if err != nil { - fmt.Printf("err: %v\n", err) - } - fmt.Printf("Add event: %s \n", y) + // fmt.Printf("Event namespace: %s \n", event.GetNamespace()) + // y, err := yaml.Marshal(event) + // if err != nil { + // fmt.Printf("err: %v\n", err) + // } + // fmt.Printf("Add event: %s \n", y) publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js) }, DeleteFunc: func(obj interface{}) { event := obj.(*v1.Event) - fmt.Printf("Delete event: %s \n", obj) + // fmt.Printf("Delete event: %s \n", obj) publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js) }, UpdateFunc: func(oldObj, newObj interface{}) { event := newObj.(*v1.Event) - fmt.Printf("Change event \n") + // fmt.Printf("Change event \n") publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js) }, }, From bd8a64c5b35cbe939096b29f565df146f85d1991 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 5 Sep 2023 23:35:24 +0530 Subject: [PATCH 014/263] wait for collect and publish function to finish and the start watching for events, this is done as our resources are very less --- agent/kubviz/k8smetrics_agent.go | 47 ++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 67d47923..be1f9a3b 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -103,9 +103,8 @@ func main() { } clientset = getK8sClient(config) } - - // starting the endless go routine to monitor the cluster - go publishMetrics(clientset, js, clusterMetricsChan) + controlChan := make(chan bool) + go publishMetrics(clientset, js, clusterMetricsChan, controlChan) collectAndPublishMetrics := func() { err := outDatedImages(config, js) @@ -123,23 +122,29 @@ func main() { LogErr(err) } + controlChan <- true collectAndPublishMetrics() + controlChan <- true if schedulingIntervalStr == "" { - schedulingIntervalStr = "20m" // Default value, e.g., 20 minutes + schedulingIntervalStr = "20m" } schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) if err != nil { log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) } s := gocron.NewScheduler(time.UTC) - s.Every(schedulingInterval).Do(collectAndPublishMetrics) // Run immediately and then at the scheduled interval - s.StartBlocking() // Blocks the main function + s.Every(schedulingInterval).Do(func() { + controlChan <- true + collectAndPublishMetrics() + controlChan <- true + }) + s.StartBlocking() } // publishMetrics publishes stream of events // with subject "METRICS.created" -func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { - watchK8sEvents(clientset, js) +func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error, controlChan <-chan bool) { + watchK8sEvents(clientset, js, controlChan) errCh <- nil } @@ -237,44 +242,44 @@ func LogErr(err error) { log.Println(err) } } -func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { +func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext, controlChan <-chan bool) { watchlist := cache.NewListWatchFromClient( clientset.CoreV1().RESTClient(), "events", v1.NamespaceAll, fields.Everything(), ) - _, controller := cache.NewInformer( // also take a look at NewSharedIndexInformer + _, controller := cache.NewInformer( watchlist, &v1.Event{}, - 0, //Duration is int64 + 0, // Duration is int64 cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { event := obj.(*v1.Event) - // fmt.Printf("Event namespace: %s \n", event.GetNamespace()) - // y, err := yaml.Marshal(event) - // if err != nil { - // fmt.Printf("err: %v\n", err) - // } - // fmt.Printf("Add event: %s \n", y) publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js) }, DeleteFunc: func(obj interface{}) { event := obj.(*v1.Event) - // fmt.Printf("Delete event: %s \n", obj) publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js) }, UpdateFunc: func(oldObj, newObj interface{}) { event := newObj.(*v1.Event) - // fmt.Printf("Change event \n") publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js) }, }, ) stop := make(chan struct{}) - defer close(stop) go controller.Run(stop) + for { - time.Sleep(time.Second) + select { + case <-controlChan: + close(stop) + <-controlChan + stop = make(chan struct{}) + go controller.Run(stop) + default: + time.Sleep(time.Second) + } } } From 5b86323f258ad786b4783623abbc0cd92984794e Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 5 Sep 2023 23:46:15 +0530 Subject: [PATCH 015/263] new code --- agent/kubviz/k8smetrics_agent.go | 1 + 1 file changed, 1 insertion(+) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index be1f9a3b..131cb435 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -73,6 +73,7 @@ func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { } func main() { + fmt.Println("new code runs...") log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production clusterMetricsChan := make(chan error, 1) From ba7b958b34acc0f09a0955fb29ea28e86d6f2a39 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 6 Sep 2023 00:14:52 +0530 Subject: [PATCH 016/263] new code --- agent/kubviz/k8smetrics_agent.go | 125 +++++++++++++++---------------- agent/kubviz/trivy.go | 4 +- 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 131cb435..33d6be52 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -30,7 +30,6 @@ import ( // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" ) // constants for jetstream @@ -74,71 +73,71 @@ func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { func main() { fmt.Println("new code runs...") - log.SetFlags(log.LstdFlags | log.Lshortfile) - env := Production - clusterMetricsChan := make(chan error, 1) - var ( - config *rest.Config - clientset *kubernetes.Clientset - ) - // connecting with nats ... - nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - checkErr(err) - // creating a jetstream connection using the nats connection - js, err := nc.JetStream() - checkErr(err) - // creating a stream with stream name METRICS - err = createStream(js) - checkErr(err) - //setupAgent() - if env != Production { - config, err = clientcmd.BuildConfigFromFlags("", cluster_conf_loc) - if err != nil { - log.Fatal(err) - } - clientset = getK8sClient(config) - } else { - config, err = rest.InClusterConfig() - if err != nil { - log.Fatal(err) - } - clientset = getK8sClient(config) - } - controlChan := make(chan bool) - go publishMetrics(clientset, js, clusterMetricsChan, controlChan) + // log.SetFlags(log.LstdFlags | log.Lshortfile) + // env := Production + // clusterMetricsChan := make(chan error, 1) + // var ( + // config *rest.Config + // clientset *kubernetes.Clientset + // ) + // // connecting with nats ... + // nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + // checkErr(err) + // // creating a jetstream connection using the nats connection + // js, err := nc.JetStream() + // checkErr(err) + // // creating a stream with stream name METRICS + // err = createStream(js) + // checkErr(err) + // //setupAgent() + // if env != Production { + // config, err = clientcmd.BuildConfigFromFlags("", cluster_conf_loc) + // if err != nil { + // log.Fatal(err) + // } + // clientset = getK8sClient(config) + // } else { + // config, err = rest.InClusterConfig() + // if err != nil { + // log.Fatal(err) + // } + // clientset = getK8sClient(config) + // } + // controlChan := make(chan bool) + // go publishMetrics(clientset, js, clusterMetricsChan, controlChan) - collectAndPublishMetrics := func() { - err := outDatedImages(config, js) - LogErr(err) - err = KubePreUpgradeDetector(config, js) - LogErr(err) - err = GetAllResources(config, js) - LogErr(err) - err = RakeesOutput(config, js) - LogErr(err) - // getK8sEvents(clientset) - err = runTrivyScans(config, js) - LogErr(err) - err = RunKubeScore(clientset, js) - LogErr(err) - } + // collectAndPublishMetrics := func() { + // err := outDatedImages(config, js) + // LogErr(err) + // err = KubePreUpgradeDetector(config, js) + // LogErr(err) + // err = GetAllResources(config, js) + // LogErr(err) + // err = RakeesOutput(config, js) + // LogErr(err) + // // getK8sEvents(clientset) + // err = runTrivyScans(config, js) + // LogErr(err) + // err = RunKubeScore(clientset, js) + // LogErr(err) + // } - controlChan <- true - collectAndPublishMetrics() - controlChan <- true - if schedulingIntervalStr == "" { - schedulingIntervalStr = "20m" - } - schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) - if err != nil { - log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) - } + // controlChan <- true + // collectAndPublishMetrics() + // controlChan <- true + // if schedulingIntervalStr == "" { + // schedulingIntervalStr = "20m" + // } + // schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) + // if err != nil { + // log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) + // } s := gocron.NewScheduler(time.UTC) - s.Every(schedulingInterval).Do(func() { - controlChan <- true - collectAndPublishMetrics() - controlChan <- true - }) + // s.Every(schedulingInterval).Do(func() { + // controlChan <- true + // collectAndPublishMetrics() + // controlChan <- true + // }) s.StartBlocking() } diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 654cf481..f5b1e165 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -23,8 +23,8 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { } log.Println("Command logs for k8s cluster scan", parts[0]) jsonPart := "{" + parts[1] - log.Println("First 200 k8s cluster scan lines output", jsonPart[:200]) - log.Println("Last 200 k8s cluster scan lines output", jsonPart[len(jsonPart)-200:]) + // log.Println("First 200 k8s cluster scan lines output", jsonPart[:200]) + // log.Println("Last 200 k8s cluster scan lines output", jsonPart[len(jsonPart)-200:]) err = json.Unmarshal([]byte(jsonPart), &report) if err != nil { log.Printf("Error occurred while Unmarshalling json for k8s cluster scan: %v", err) From 61bdb5983eb305d463230e0151135c702a4a3dff Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 6 Sep 2023 00:21:59 +0530 Subject: [PATCH 017/263] new code --- agent/kubviz/k8smetrics_agent.go | 125 ++++++++++++++++--------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 33d6be52..131cb435 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -30,6 +30,7 @@ import ( // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/clientcmd" ) // constants for jetstream @@ -73,71 +74,71 @@ func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { func main() { fmt.Println("new code runs...") - // log.SetFlags(log.LstdFlags | log.Lshortfile) - // env := Production - // clusterMetricsChan := make(chan error, 1) - // var ( - // config *rest.Config - // clientset *kubernetes.Clientset - // ) - // // connecting with nats ... - // nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - // checkErr(err) - // // creating a jetstream connection using the nats connection - // js, err := nc.JetStream() - // checkErr(err) - // // creating a stream with stream name METRICS - // err = createStream(js) - // checkErr(err) - // //setupAgent() - // if env != Production { - // config, err = clientcmd.BuildConfigFromFlags("", cluster_conf_loc) - // if err != nil { - // log.Fatal(err) - // } - // clientset = getK8sClient(config) - // } else { - // config, err = rest.InClusterConfig() - // if err != nil { - // log.Fatal(err) - // } - // clientset = getK8sClient(config) - // } - // controlChan := make(chan bool) - // go publishMetrics(clientset, js, clusterMetricsChan, controlChan) + log.SetFlags(log.LstdFlags | log.Lshortfile) + env := Production + clusterMetricsChan := make(chan error, 1) + var ( + config *rest.Config + clientset *kubernetes.Clientset + ) + // connecting with nats ... + nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + checkErr(err) + // creating a jetstream connection using the nats connection + js, err := nc.JetStream() + checkErr(err) + // creating a stream with stream name METRICS + err = createStream(js) + checkErr(err) + //setupAgent() + if env != Production { + config, err = clientcmd.BuildConfigFromFlags("", cluster_conf_loc) + if err != nil { + log.Fatal(err) + } + clientset = getK8sClient(config) + } else { + config, err = rest.InClusterConfig() + if err != nil { + log.Fatal(err) + } + clientset = getK8sClient(config) + } + controlChan := make(chan bool) + go publishMetrics(clientset, js, clusterMetricsChan, controlChan) - // collectAndPublishMetrics := func() { - // err := outDatedImages(config, js) - // LogErr(err) - // err = KubePreUpgradeDetector(config, js) - // LogErr(err) - // err = GetAllResources(config, js) - // LogErr(err) - // err = RakeesOutput(config, js) - // LogErr(err) - // // getK8sEvents(clientset) - // err = runTrivyScans(config, js) - // LogErr(err) - // err = RunKubeScore(clientset, js) - // LogErr(err) - // } + collectAndPublishMetrics := func() { + err := outDatedImages(config, js) + LogErr(err) + err = KubePreUpgradeDetector(config, js) + LogErr(err) + err = GetAllResources(config, js) + LogErr(err) + err = RakeesOutput(config, js) + LogErr(err) + // getK8sEvents(clientset) + err = runTrivyScans(config, js) + LogErr(err) + err = RunKubeScore(clientset, js) + LogErr(err) + } - // controlChan <- true - // collectAndPublishMetrics() - // controlChan <- true - // if schedulingIntervalStr == "" { - // schedulingIntervalStr = "20m" - // } - // schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) - // if err != nil { - // log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) - // } + controlChan <- true + collectAndPublishMetrics() + controlChan <- true + if schedulingIntervalStr == "" { + schedulingIntervalStr = "20m" + } + schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) + if err != nil { + log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) + } s := gocron.NewScheduler(time.UTC) - // s.Every(schedulingInterval).Do(func() { - // controlChan <- true - // collectAndPublishMetrics() - // controlChan <- true - // }) + s.Every(schedulingInterval).Do(func() { + controlChan <- true + collectAndPublishMetrics() + controlChan <- true + }) s.StartBlocking() } From b4534a40c2c658eceec5bb9f8c77a2f58a80ce4e Mon Sep 17 00:00:00 2001 From: vijeyash <91282703+vijeyash1@users.noreply.github.com> Date: Wed, 6 Sep 2023 01:02:25 +0530 Subject: [PATCH 018/263] Update k8smetrics_agent.go Signed-off-by: vijeyash <91282703+vijeyash1@users.noreply.github.com> --- agent/kubviz/k8smetrics_agent.go | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 131cb435..be1f9a3b 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -73,7 +73,6 @@ func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { } func main() { - fmt.Println("new code runs...") log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production clusterMetricsChan := make(chan error, 1) From 8c2dca8816a26a870b14452e16d719afea912404 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 6 Sep 2023 11:16:54 +0530 Subject: [PATCH 019/263] removed unwanted logs --- agent/kubviz/trivy.go | 4 ++-- agent/kubviz/trivy_image.go | 6 +++--- agent/kubviz/trivy_sbom.go | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index f5b1e165..adaf84ef 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -15,13 +15,13 @@ import ( func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport out, err := executeCommand("trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") - log.Println("Commnd for k8s cluster scan: trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") + // log.Println("Commnd for k8s cluster scan: trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") parts := strings.SplitN(out, "{", 2) if len(parts) <= 1 { log.Println("No output from k8s cluster scan command", err) return err } - log.Println("Command logs for k8s cluster scan", parts[0]) + // log.Println("Command logs for k8s cluster scan", parts[0]) jsonPart := "{" + parts[1] // log.Println("First 200 k8s cluster scan lines output", jsonPart[:200]) // log.Println("Last 200 k8s cluster scan lines output", jsonPart[len(jsonPart)-200:]) diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index 4eaac417..48081e38 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -33,10 +33,10 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image if there's no output } - log.Println("Command logs for image", parts[0]) + // log.Println("Command logs for image", parts[0]) jsonPart := "{" + parts[1] - log.Println("First 200 image scan lines output", jsonPart[:200]) - log.Println("Last 200 image scan lines output", jsonPart[len(jsonPart)-200:]) + // log.Println("First 200 image scan lines output", jsonPart[:200]) + // log.Println("Last 200 image scan lines output", jsonPart[len(jsonPart)-200:]) err = json.Unmarshal([]byte(jsonPart), &report) if err != nil { diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index d33d2709..b12c07d3 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -52,7 +52,6 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("failed to list images: %v", err) } for _, image := range images { - fmt.Printf("pullable Image %#v\n", image.PullableImage) command := fmt.Sprintf("trivy image --format cyclonedx %s %s", image.PullableImage, "--cache-dir /tmp/.cache") out, err := executeCommandSbom(command) @@ -74,7 +73,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error unmarshaling JSON data for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error } - log.Println("report", report) + // log.Println("report", report) // Publish the report using the given function publishTrivySbomReport(report, js) From 826a03601b7f6995e67d059561690da84ed21497 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Thu, 7 Sep 2023 10:14:49 +0530 Subject: [PATCH 020/263] changed logs --- agent/kubviz/k8smetrics_agent.go | 2 +- agent/kubviz/trivy_sbom.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 131cb435..d3e76f0f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -161,7 +161,7 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre if err != nil { return true, err } - // log.Printf("Metrics with ID:%s has been published\n", id) + log.Printf("Metrics with ID:%s has been published\n", id) return false, nil } diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index b12c07d3..c0b697aa 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -25,7 +25,7 @@ func publishTrivySbomReport(report model.Sbom, js nats.JetStreamContext) error { return err } - log.Printf("Trivy report with BomFormat:%v has been published\n", metrics.Report.BomFormat) + log.Printf("Trivy report with Id %v has been published\n", metrics.ID) return nil } From 0cb54e5b6b9d2b20ac1deaaa6d6896c9d5c0217a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Thu, 7 Sep 2023 10:25:20 +0530 Subject: [PATCH 021/263] removed pause and resume for events --- agent/kubviz/k8smetrics_agent.go | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index d3e76f0f..89a3e7f4 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -104,8 +104,7 @@ func main() { } clientset = getK8sClient(config) } - controlChan := make(chan bool) - go publishMetrics(clientset, js, clusterMetricsChan, controlChan) + go publishMetrics(clientset, js, clusterMetricsChan) collectAndPublishMetrics := func() { err := outDatedImages(config, js) @@ -123,9 +122,7 @@ func main() { LogErr(err) } - controlChan <- true collectAndPublishMetrics() - controlChan <- true if schedulingIntervalStr == "" { schedulingIntervalStr = "20m" } @@ -135,17 +132,15 @@ func main() { } s := gocron.NewScheduler(time.UTC) s.Every(schedulingInterval).Do(func() { - controlChan <- true collectAndPublishMetrics() - controlChan <- true }) s.StartBlocking() } // publishMetrics publishes stream of events // with subject "METRICS.created" -func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error, controlChan <-chan bool) { - watchK8sEvents(clientset, js, controlChan) +func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { + watchK8sEvents(clientset, js) errCh <- nil } @@ -243,7 +238,7 @@ func LogErr(err error) { log.Println(err) } } -func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext, controlChan <-chan bool) { +func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { watchlist := cache.NewListWatchFromClient( clientset.CoreV1().RESTClient(), "events", @@ -270,17 +265,10 @@ func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext, c }, ) stop := make(chan struct{}) + defer close(stop) go controller.Run(stop) for { - select { - case <-controlChan: - close(stop) - <-controlChan - stop = make(chan struct{}) - go controller.Run(stop) - default: - time.Sleep(time.Second) - } + time.Sleep(time.Second) } } From c62dde9edf5bd4041e01254a39b86513c1e72077 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sat, 9 Sep 2023 10:35:21 +0530 Subject: [PATCH 022/263] modified all schemas for including datetime in utc --- client/pkg/clickhouse/db_client.go | 54 ++++++++++++-- client/pkg/clickhouse/statements.go | 89 +++++++++++++---------- gitmodels/dbstatement/dbstatement.go | 101 ++++++++++++++------------- test.txt | 28 ++++++++ 4 files changed, 180 insertions(+), 92 deletions(-) create mode 100644 test.txt diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index de3fec40..c149bf2e 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -74,7 +74,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { return nil, err } - tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerDockerhubTable, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, quayContainerPushEventTable, jfrogContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} + tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, quayContainerPushEventTable, jfrogContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} for _, table := range tables { if err = splconn.Exec(context.Background(), string(table)); err != nil { return nil, err @@ -93,12 +93,16 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { } return &DBClient{splconn: splconn, conn: stdconn, conf: conf}, nil } + func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushEventPayload) { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(string(InsertAzureContainerPushEvent)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + registryURL := pushEvent.Request.Host repositoryName := pushEvent.Target.Repository tag := pushEvent.Target.Tag @@ -110,7 +114,6 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE size := pushEvent.Target.Size shaID := pushEvent.Target.Digest - // Marshaling the pushEvent into a JSON string pushEventJSON, err := json.Marshal(pushEvent) if err != nil { log.Printf("Error while marshaling Azure Container Registry payload: %v", err) @@ -126,6 +129,7 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE pushEvent.Timestamp, size, shaID, + currentTime, ); err != nil { log.Fatal(err) } @@ -133,15 +137,18 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE log.Fatal(err) } } + func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(string(InsertQuayContainerPushEvent)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + dockerURL := pushEvent.DockerURL repository := pushEvent.Repository - //tag := pushEvent.UpdatedTags name := pushEvent.Name nameSpace := pushEvent.Namespace homePage := pushEvent.Homepage @@ -153,7 +160,6 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload tag = "" } - // Marshaling the pushEvent into a JSON string pushEventJSON, err := json.Marshal(pushEvent) if err != nil { log.Printf("Error while marshaling Quay Container Registry payload: %v", err) @@ -168,6 +174,7 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload homePage, tag, string(pushEventJSON), + currentTime, ); err != nil { log.Fatal(err) } @@ -175,12 +182,16 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload log.Fatal(err) } } + func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(string(InsertJfrogContainerPushEvent)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + registryURL := pushEvent.Data.Path repositoryName := pushEvent.Data.Name tag := pushEvent.Data.Tag @@ -192,7 +203,6 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE size := pushEvent.Data.Size shaID := pushEvent.Data.SHA256 - // Marshaling the pushEvent into a JSON string pushEventJSON, err := json.Marshal(pushEvent) if err != nil { log.Printf("Error while marshaling Jfrog Container Registry payload: %v", err) @@ -209,6 +219,7 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE imageName, tag, string(pushEventJSON), + currentTime, ); err != nil { log.Fatal(err) } @@ -216,12 +227,16 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE log.Fatal(err) } } + func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(string(InsertRakees)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( metrics.ClusterName, metrics.Name, @@ -229,6 +244,7 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { metrics.Delete, metrics.List, metrics.Update, + currentTime, ); err != nil { log.Fatal(err) } @@ -236,18 +252,23 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { log.Fatal(err) } } + func (c *DBClient) InsertKetallEvent(metrics model.Resource) { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(string(InsertKetall)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( metrics.ClusterName, metrics.Namespace, metrics.Kind, metrics.Resource, metrics.Age, + currentTime, ); err != nil { log.Fatal(err) } @@ -255,12 +276,16 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { log.Fatal(err) } } + func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(string(InsertOutdated)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( metrics.ClusterName, metrics.Namespace, @@ -269,6 +294,7 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { metrics.Current, metrics.LatestVersion, metrics.VersionsBehind, + currentTime, ); err != nil { log.Fatal(err) } @@ -276,6 +302,7 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { log.Fatal(err) } } + func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { var ( tx, _ = c.conn.Begin() @@ -288,6 +315,8 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { deprecated = 1 } + currentTime := time.Now().UTC() + for _, item := range deprecatedAPI.Items { if _, err := stmt.Exec( deprecatedAPI.ClusterName, @@ -296,6 +325,7 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { deprecatedAPI.Kind, deprecated, item.Scope, + currentTime, ); err != nil { log.Fatal(err) } @@ -305,6 +335,7 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { log.Fatal(err) } } + func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { var ( tx, _ = c.conn.Begin() @@ -316,6 +347,8 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { deleted = 1 } + currentTime := time.Now().UTC() + for _, item := range deletedAPI.Items { if _, err := stmt.Exec( deletedAPI.ClusterName, @@ -326,6 +359,7 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { deletedAPI.Name, deleted, item.Scope, + currentTime, ); err != nil { log.Fatal(err) } @@ -335,6 +369,7 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { log.Fatal(err) } } + func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { var ( tx, _ = c.conn.Begin() @@ -674,6 +709,9 @@ func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { stmt, _ = tx.Prepare(string(InsertDockerHubBuild)) ) defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( build.PushedBy, build.ImageTag, @@ -681,6 +719,7 @@ func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { build.DateCreated, build.Owner, build.Event, + currentTime, ); err != nil { log.Fatal(err) } @@ -737,6 +776,9 @@ func (c *DBClient) InsertGitCommon(metrics model.GitCommonAttribute, statement d return err } defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( metrics.Author, metrics.GitProvider, @@ -744,7 +786,7 @@ func (c *DBClient) InsertGitCommon(metrics model.GitCommonAttribute, statement d metrics.CommitUrl, metrics.EventType, metrics.RepoName, - metrics.TimeStamp, + currentTime, metrics.Event, ); err != nil { return err diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 9a6c300d..d6a487e4 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -26,9 +26,11 @@ CREATE TABLE IF NOT EXISTS rakkess ( Create String, Delete String, List String, - Update String + Update String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const kubePugDepricatedTable DBStatement = ` CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( ClusterName String, @@ -36,9 +38,11 @@ CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( Description String, Kind String, Deprecated UInt8, - Scope String + Scope String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const kubepugDeletedTable DBStatement = ` CREATE TABLE IF NOT EXISTS DeletedAPIs ( ClusterName String, @@ -48,31 +52,37 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( Version String, Name String, Deleted UInt8, - Scope String + Scope String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const jfrogContainerPushEventTable DBStatement = ` - CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( - Domain String, - EventType String, - RegistryURL String, - RepositoryName String, - SHAID String, - Size Int32, - ImageName String, - Tag String, - Event String - ) engine=File(TabSeparated) - ` -const ketallTable DBStatement = ` -CREATE TABLE IF NOT EXISTS getall_resources ( - ClusterName String, - Namespace String, - Kind String, - Resource String, - Age String +CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( + Domain String, + EventType String, + RegistryURL String, + RepositoryName String, + SHAID String, + Size Int32, + ImageName String, + Tag String, + Event String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + +const ketallTable DBStatement = ` + CREATE TABLE IF NOT EXISTS getall_resources ( + ClusterName String, + Namespace String, + Kind String, + Resource String, + Age String, + EventTime DateTime('UTC') + ) engine=File(TabSeparated) + ` + const outdateTable DBStatement = ` CREATE TABLE IF NOT EXISTS outdated_images ( ClusterName String, @@ -81,9 +91,11 @@ CREATE TABLE IF NOT EXISTS outdated_images ( CurrentImage String, CurrentTag String, LatestVersion String, - VersionsBehind Int64 + VersionsBehind Int64, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const kubescoreTable DBStatement = ` CREATE TABLE IF NOT EXISTS kubescore ( id UUID, @@ -155,9 +167,11 @@ const dockerHubBuildTable DBStatement = ` RepositoryName String, DateCreated String, Owner String, - Event String + Event String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const azureContainerPushEventTable DBStatement = ` CREATE TABLE IF NOT EXISTS azurecontainerpush ( RegistryURL String, @@ -167,9 +181,11 @@ const azureContainerPushEventTable DBStatement = ` Event String, Timestamp String, Size Int32, - SHAID String + SHAID String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const quayContainerPushEventTable DBStatement = ` CREATE TABLE IF NOT EXISTS quaycontainerpush ( name String, @@ -178,9 +194,11 @@ const quayContainerPushEventTable DBStatement = ` dockerURL String, homePage String, tag String, - Event String + Event String, + EventTime DateTime('UTC') ) engine=File(TabSeparated) ` + const trivySbomTable DBStatement = ` CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, @@ -207,21 +225,20 @@ const trivySbomTable DBStatement = ` ) engine=File(TabSeparated) ` -const InsertDockerHubBuild DBStatement = "INSERT INTO dockerhubbuild (PushedBy, ImageTag, RepositoryName, DateCreated, Owner, Event) VALUES (?, ?, ?, ?, ?, ?)" -const InsertRakees DBStatement = "INSERT INTO rakkess (ClusterName, Name, Create, Delete, List, Update) VALUES (?, ?, ?, ?, ?, ?)" -const InsertKetall DBStatement = "INSERT INTO getall_resources (ClusterName, Namespace, Kind, Resource, Age) VALUES (?, ?, ?, ?, ?)" -const InsertOutdated DBStatement = "INSERT INTO outdated_images (ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind) VALUES (?, ?, ?, ?, ?, ?, ?)" -const InsertDepricatedApi DBStatement = "INSERT INTO DeprecatedAPIs (ClusterName, ObjectName, Description, Kind, Deprecated, Scope) VALUES (?, ?, ?, ?, ?, ?)" -const InsertDeletedApi DBStatement = "INSERT INTO DeletedAPIs (ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" +const InsertDockerHubBuild DBStatement = "INSERT INTO dockerhubbuild (PushedBy, ImageTag, RepositoryName, DateCreated, Owner, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?)" +const InsertRakees DBStatement = "INSERT INTO rakkess (ClusterName, Name, Create, Delete, List, Update, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?)" +const InsertKetall DBStatement = "INSERT INTO getall_resources (ClusterName, Namespace, Kind, Resource, Age, EventTime) VALUES (?, ?, ?, ?, ?, ?)" +const InsertOutdated DBStatement = "INSERT INTO outdated_images (ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" +const InsertDepricatedApi DBStatement = "INSERT INTO DeprecatedAPIs (ClusterName, ObjectName, Description, Kind, Deprecated, Scope, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?)" +const InsertDeletedApi DBStatement = "INSERT INTO DeletedAPIs (ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertKubvizEvent DBStatement = "INSERT INTO events (ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, FirstTime, LastTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const clickhouseExperimental DBStatement = `SET allow_experimental_object_type=1;` -const containerDockerhubTable DBStatement = `CREATE table IF NOT EXISTS container_dockerhub(event JSON) ENGINE = MergeTree ORDER BY tuple();` const containerGithubTable DBStatement = `CREATE table IF NOT EXISTS container_github(event JSON) ENGINE = MergeTree ORDER BY tuple();` const InsertKubeScore string = "INSERT INTO kubescore (id, namespace, cluster_name, recommendations) VALUES (?, ?, ?, ?)" const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?)" const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?, ?)" -const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Timestamp, Size, SHAID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" +const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Timestamp, Size, SHAID, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivySbom string = "INSERT INTO trivysbom (id, schema, bom_format,spec_version,serial_number, version, metadata_timestamp,metatool_vendor,metatool_name,metatool_version,component_bom_ref,component_type,component_name,component_version,component_property_name,component_property_value,component_hash_alg,component_hash_content,component_license_exp,component_purl,dependency_ref) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" -const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage,tag, Event) VALUES (?, ?, ?, ?, ?, ?, ?)" -const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType,RegistryURL, RepositoryName,SHAID, Size, ImageName ,Tag, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?)" +const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" +const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/gitmodels/dbstatement/dbstatement.go b/gitmodels/dbstatement/dbstatement.go index c51d2236..09f67b28 100644 --- a/gitmodels/dbstatement/dbstatement.go +++ b/gitmodels/dbstatement/dbstatement.go @@ -3,75 +3,76 @@ package dbstatement type DBStatement string const AzureDevopsTable DBStatement = ` - CREATE TABLE IF NOT EXISTS azure_devops ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp String, - Event String - ) engine=File(TabSeparated) +CREATE TABLE IF NOT EXISTS azure_devops ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated) ` const InsertAzureDevops DBStatement = "INSERT INTO azure_devops ( Author, Provider, CommitID, CommitUrl, EventType, RepoName, TimeStamp, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const GithubTable DBStatement = ` - CREATE TABLE IF NOT EXISTS github ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp String, - Event String - ) engine=File(TabSeparated) +CREATE TABLE IF NOT EXISTS github ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated) ` const InsertGithub DBStatement = "INSERT INTO github ( Author, Provider, CommitID, CommitUrl, EventType, RepoName, TimeStamp, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const GitlabTable DBStatement = ` - CREATE TABLE IF NOT EXISTS gitlab ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp String, - Event String - ) engine=File(TabSeparated) +CREATE TABLE IF NOT EXISTS gitlab ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated) ` + const InsertGitlab DBStatement = "INSERT INTO gitlab ( Author, Provider, CommitID, CommitUrl, EventType, RepoName, TimeStamp, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const BitbucketTable DBStatement = ` - CREATE TABLE IF NOT EXISTS bitbucket ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp String, - Event String - ) engine=File(TabSeparated) +CREATE TABLE IF NOT EXISTS bitbucket ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated) ` const InsertBitbucket DBStatement = "INSERT INTO bitbucket ( Author, Provider, CommitID, CommitUrl, EventType, RepoName, TimeStamp, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const GiteaTable DBStatement = ` - CREATE TABLE IF NOT EXISTS gitea ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp String, - Event String - ) engine=File(TabSeparated) +CREATE TABLE IF NOT EXISTS gitea ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated) ` const InsertGitea DBStatement = "INSERT INTO gitea ( Author, Provider, CommitID, CommitUrl, EventType, RepoName, TimeStamp, Event) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/test.txt b/test.txt new file mode 100644 index 00000000..cf079bb9 --- /dev/null +++ b/test.txt @@ -0,0 +1,28 @@ +kubviz-tables : + +events - have datetime +trivy_vul - have datetime +trivysbom - have datetime +trivyimage - have datetime +trivy_misconfig - need to include time field in schema +rakkess - need to include time field in schema +DeprecatedAPIs - need to include time field in schema +DeletedAPIs - have datetime +getall_resources - need to include time field in schema +outdated_images - need to include time field in schema +kubescore - need to include time field in schema + +container-bridge tables: + +dockerhubbuild - need to include time field in schema +quaycontainerpush - need to include time field in schema +jfrogcontainerpush - need to include time field in schema +azurecontainerpush - time is in string + +git-bridge tables : + +azure_devops - time is in string +github - time is in string +gitlab - time is in string +bitbucket - time is in string +gitea - time is in string From 877f5804b8ba49db603044b59e3ede9cf6e3d691 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sat, 9 Sep 2023 10:57:00 +0530 Subject: [PATCH 023/263] minor fix --- client/pkg/clickhouse/db_client.go | 6 +++++ client/pkg/clickhouse/statements.go | 38 +++++++++++++++-------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index c149bf2e..35f96b6d 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -484,11 +484,16 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { } stmt.Close() } + for _, misconfiguration := range result.Misconfigurations { var ( tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(InsertTrivyMisconfig) ) + defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( metrics.ID, metrics.ClusterName, @@ -505,6 +510,7 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { misconfiguration.Resolution, misconfiguration.Severity, string(misconfiguration.Status), + currentTime, ); err != nil { log.Fatal(err) } diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index d6a487e4..ef491b52 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -126,24 +126,26 @@ const trivyTableVul DBStatement = ` ` const trivyTableMisconfig DBStatement = ` - CREATE TABLE IF NOT EXISTS trivy_misconfig ( - id UUID, - cluster_name String, - namespace String, - kind String, - name String, - misconfig_id String, - misconfig_avdid String, - misconfig_type String, - misconfig_title String, - misconfig_desc String, - misconfig_msg String, - misconfig_query String, - misconfig_resolution String, - misconfig_severity String, - misconfig_status String - ) engine=File(TabSeparated) + CREATE TABLE IF NOT EXISTS trivy_misconfig ( + id UUID, + cluster_name String, + namespace String, + kind String, + name String, + misconfig_id String, + misconfig_avdid String, + misconfig_type String, + misconfig_title String, + misconfig_desc String, + misconfig_msg String, + misconfig_query String, + misconfig_resolution String, + misconfig_severity String, + misconfig_status String, + EventTime DateTime('UTC') + ) engine=File(TabSeparated) ` + const trivyTableImage DBStatement = ` CREATE TABLE IF NOT EXISTS trivyimage ( id UUID, @@ -237,7 +239,7 @@ const containerGithubTable DBStatement = `CREATE table IF NOT EXISTS container_g const InsertKubeScore string = "INSERT INTO kubescore (id, namespace, cluster_name, recommendations) VALUES (?, ?, ?, ?)" const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?)" const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?, ?)" +const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Timestamp, Size, SHAID, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivySbom string = "INSERT INTO trivysbom (id, schema, bom_format,spec_version,serial_number, version, metadata_timestamp,metatool_vendor,metatool_name,metatool_version,component_bom_ref,component_type,component_name,component_version,component_property_name,component_property_value,component_hash_alg,component_hash_content,component_license_exp,component_purl,dependency_ref) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" From 5171b214c6fe1bdc364abfd4a2403cc32ebc82ac Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sat, 9 Sep 2023 11:01:48 +0530 Subject: [PATCH 024/263] minor fix --- client/pkg/clickhouse/db_client.go | 1 - 1 file changed, 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 35f96b6d..3152756f 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -517,7 +517,6 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { if err := tx.Commit(); err != nil { log.Fatal(err) } - stmt.Close() } } } From 933c2e474fc7f119fcc45b01a87c17854e0305a7 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sat, 9 Sep 2023 15:28:27 +0530 Subject: [PATCH 025/263] added datetime to kubescore --- client/pkg/clickhouse/db_client.go | 4 ++++ client/pkg/clickhouse/statements.go | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 3152756f..c613c4b1 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -438,11 +438,15 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations stmt, _ = tx.Prepare(InsertKubeScore) ) defer stmt.Close() + + currentTime := time.Now().UTC() + if _, err := stmt.Exec( metrics.ID, metrics.Namespace, metrics.ClusterName, metrics.Recommendations, + currentTime, ); err != nil { log.Fatal(err) } diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index ef491b52..eef44eb5 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -97,13 +97,15 @@ CREATE TABLE IF NOT EXISTS outdated_images ( ` const kubescoreTable DBStatement = ` - CREATE TABLE IF NOT EXISTS kubescore ( - id UUID, - namespace String, - cluster_name String, - recommendations String - ) engine=File(TabSeparated) - ` +CREATE TABLE IF NOT EXISTS kubescore ( + id UUID, + namespace String, + cluster_name String, + recommendations String, + EventTime DateTime('UTC') +) engine=File(TabSeparated) +` + const trivyTableVul DBStatement = ` CREATE TABLE IF NOT EXISTS trivy_vul ( id UUID, @@ -236,7 +238,7 @@ const InsertDeletedApi DBStatement = "INSERT INTO DeletedAPIs (ClusterName, Obje const InsertKubvizEvent DBStatement = "INSERT INTO events (ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, FirstTime, LastTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const clickhouseExperimental DBStatement = `SET allow_experimental_object_type=1;` const containerGithubTable DBStatement = `CREATE table IF NOT EXISTS container_github(event JSON) ENGINE = MergeTree ORDER BY tuple();` -const InsertKubeScore string = "INSERT INTO kubescore (id, namespace, cluster_name, recommendations) VALUES (?, ?, ?, ?)" +const InsertKubeScore string = "INSERT INTO kubescore (id, namespace, cluster_name, recommendations, EventTime) VALUES (?, ?, ?, ?, ?)" const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?)" const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" From de25e7ccef6950141fa5a69b432146a62f15579d Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Sat, 9 Sep 2023 19:39:57 +0530 Subject: [PATCH 026/263] dashboards including time series --- grafana/containerBridge-dashboard.json | 329 +++++++++++ grafana/gitBridge-dashboard.json | 584 +++++++----------- grafana/kubeData-dashboard.json | 5 +- grafana/kubeScore-dashboard.json | 24 +- grafana/kubvizDsahboard.json | 538 +++++++++-------- grafana/kubvizFeatures-dashboard.json | 22 +- grafana/trivy-dashboard.json | 783 +++++++++++++++++-------- 7 files changed, 1405 insertions(+), 880 deletions(-) create mode 100644 grafana/containerBridge-dashboard.json diff --git a/grafana/containerBridge-dashboard.json b/grafana/containerBridge-dashboard.json new file mode 100644 index 00000000..7ebca535 --- /dev/null +++ b/grafana/containerBridge-dashboard.json @@ -0,0 +1,329 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 10, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.quaycontainerpush\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime", + "rawQuery": "SELECT * FROM default.quaycontainerpush\nWHERE EventTime >= toDateTime(1694165676) AND EventTime <= toDateTime(1694252076)\nORDER BY EventTime", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Quay Container Registry", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.jfrogcontainerpush\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.jfrogcontainerpush\nWHERE EventTime >= toDateTime(1694226084) AND EventTime <= toDateTime(1694247684)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "JFrog Container Registry", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.azurecontainerpush\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.azurecontainerpush\nWHERE EventTime >= toDateTime(1694165623) AND EventTime <= toDateTime(1694252023)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Container Registry", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.dockerhubbuild\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC\n", + "rawQuery": "SELECT * FROM default.dockerhubbuild\nWHERE EventTime >= toDateTime(1694165564) AND EventTime <= toDateTime(1694251964)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "DockerHub", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Container-Bridge", + "uid": "cf8cf066-b241-48c8-9e3d-863eed33bcf3", + "version": 2, + "weekStart": "" +} \ No newline at end of file diff --git a/grafana/gitBridge-dashboard.json b/grafana/gitBridge-dashboard.json index ed3c6d0f..2f9b61c6 100644 --- a/grafana/gitBridge-dashboard.json +++ b/grafana/gitBridge-dashboard.json @@ -81,8 +81,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694224447) AND TimeStamp <= toDateTime(1694246047) AND EventType = 'push'", "refId": "A", "round": "0s", "skip_comments": true @@ -148,8 +148,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", - "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", + "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'", + "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694224469) AND TimeStamp <= toDateTime(1694246069) AND EventType = 'Push Hook'", "refId": "A", "round": "0s", "skip_comments": true @@ -215,8 +215,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224493) AND TimeStamp <= toDateTime(1694246093) AND EventType = 'repo:push'", "refId": "A", "round": "0s", "skip_comments": true @@ -282,8 +282,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", - "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", + "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", + "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224513) AND TimeStamp <= toDateTime(1694246113) AND EventType = 'push'", "refId": "A", "round": "0s", "skip_comments": true @@ -349,8 +349,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224533) AND TimeStamp <= toDateTime(1694246133) AND EventType = 'git.push'", "refId": "A", "round": "0s", "skip_comments": true @@ -416,8 +416,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694224285) AND TimeStamp <= toDateTime(1694245885) AND EventType = 'pull_request'", "refId": "A", "round": "0s", "skip_comments": true @@ -483,8 +483,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", - "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", + "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'", + "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694224307) AND TimeStamp <= toDateTime(1694245907) AND EventType = 'Merge Request Hook'", "refId": "A", "round": "0s", "skip_comments": true @@ -550,8 +550,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224327) AND TimeStamp <= toDateTime(1694245927) AND EventType = 'pullrequest:fulfilled'", "refId": "A", "round": "0s", "skip_comments": true @@ -617,8 +617,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", + "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224386) AND TimeStamp <= toDateTime(1694245986) AND EventType = 'pull_request'", "refId": "A", "round": "0s", "skip_comments": true @@ -684,8 +684,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224413) AND TimeStamp <= toDateTime(1694246013) AND EventType = 'git.pullrequest.merged'", "refId": "A", "round": "0s", "skip_comments": true @@ -697,8 +697,8 @@ }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of Push events on Azure by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -777,31 +777,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Azure_Push_Events\nFROM default.azure_devops\nWHERE EventType = 'git.push'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Azure_Push_Events\nFROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Azure_Push_Events\nFROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224224) AND TimeStamp <= toDateTime(1694245824) AND EventType = 'git.push'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Azure Push Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of Merge events on Azure by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -880,31 +877,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Azure_Merge_Events\nFROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Azure_Merge_Events\nFROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Azure_Merge_Events\nFROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224264) AND TimeStamp <= toDateTime(1694245864) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Azure Merge Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of Push events on BitBucket by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -983,31 +977,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE EventType = 'repo:push'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224141) AND TimeStamp <= toDateTime(1694245741) AND EventType = 'repo:push'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "BitBucket Push Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of Merge events on BitBucket by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1086,31 +1077,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224185) AND TimeStamp <= toDateTime(1694245785) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "BitBucket Merge Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of Push events on GiTea by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1189,31 +1177,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE EventType = 'push'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224046) AND TimeStamp <= toDateTime(1694245646) AND EventType = 'push'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GiTea Push Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of Merge events on GiTea by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1291,45 +1276,29 @@ }, "targets": [ { - "builderOptions": { - "database": "default", - "fields": [], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [], - "table": "" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "database": "default", - "fields": [], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [], - "table": "" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE EventType = 'pull_request'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224094) AND TimeStamp <= toDateTime(1694245694) AND EventType = 'pull_request'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GiTea Merge Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of push events on GitLab by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1408,31 +1377,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE EventType = 'Push Hook'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694223949) AND TimeStamp <= toDateTime(1694245549) AND EventType = 'Push Hook'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GitLab Push Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of merge events on GitLab by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1511,31 +1477,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE EventType = 'Merge Request Hook'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694224003) AND TimeStamp <= toDateTime(1694245603) AND EventType = 'Merge Request Hook'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GitLab Merge Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of push events on Github by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1613,31 +1576,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE EventType = 'push'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694223871) AND TimeStamp <= toDateTime(1694245471) AND EventType = 'push'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Github Push Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "The panel displays the count of merge events on Github by each author. It provides insights into the contribution activity of different authors within the specified repository.", "fieldConfig": { @@ -1715,31 +1675,28 @@ "targets": [ { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE EventType = 'pull_request'\nGROUP BY Author", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694223910) AND TimeStamp <= toDateTime(1694245510) AND EventType = 'pull_request'\nGROUP BY Author", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Github Merge Events by Author", - "transparent": true, "type": "barchart" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "This panel displays all the events occured in the Azure Procider", "fieldConfig": { @@ -1762,10 +1719,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -1794,46 +1747,29 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "azure_devops" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"azure_devops\" ORDER BY TimeStamp DESC LIMIT 100", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694223793) AND TimeStamp <= toDateTime(1694245393)\nORDER BY TimeStamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Azure", - "transparent": true, "type": "table" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "This panel displays all the events occured in the BitBucket Providers", "fieldConfig": { @@ -1856,10 +1792,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -1888,46 +1820,29 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "bitbucket" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"bitbucket\" ORDER BY TimeStamp DESC LIMIT 100", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694223708) AND TimeStamp <= toDateTime(1694245308)\nORDER BY TimeStamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "BitBucket", - "transparent": true, "type": "table" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "This panel displays all the events occured in the GiTea provider", "fieldConfig": { @@ -1950,10 +1865,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -1982,46 +1893,29 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "gitea" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"gitea\" ORDER BY TimeStamp DESC LIMIT 100", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694223444) AND TimeStamp <= toDateTime(1694245044)\nORDER BY TimeStamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GiTea", - "transparent": true, "type": "table" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "This panel displays all the events occured in the GitLab provider.", "fieldConfig": { @@ -2044,10 +1938,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -2076,46 +1966,29 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "gitlab" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"gitlab\" ORDER BY TimeStamp DESC LIMIT 100", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694223029) AND TimeStamp <= toDateTime(1694244629)\nORDER BY TimeStamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GitLab", - "transparent": true, "type": "table" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "description": "This panel displays all the events occured in the GitHub provider.", "fieldConfig": { @@ -2138,10 +2011,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -2170,40 +2039,23 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "github" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"github\" ORDER BY TimeStamp DESC LIMIT 100", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.github\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.github\nWHERE TimeStamp >= toDateTime(1694222878) AND TimeStamp <= toDateTime(1694244478)\nORDER BY TimeStamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "GitHub", - "transparent": true, "type": "table" } ], @@ -2215,13 +2067,13 @@ "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "GitBridge", "uid": "u3EJcUqVk", - "version": 1, + "version": 2, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/kubeData-dashboard.json b/grafana/kubeData-dashboard.json index 53d1b521..7768bb2e 100644 --- a/grafana/kubeData-dashboard.json +++ b/grafana/kubeData-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, + "id": 5, "links": [], "liveNow": false, "panels": [ @@ -172,7 +172,6 @@ } ], "title": "Kubernetes", - "transparent": true, "type": "table" } ], @@ -286,4 +285,4 @@ "uid": "Qq-FK1rVz", "version": 1, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/kubeScore-dashboard.json b/grafana/kubeScore-dashboard.json index b84ecb42..2fb3a6a0 100644 --- a/grafana/kubeScore-dashboard.json +++ b/grafana/kubeScore-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 10, + "id": 5, "links": [], "liveNow": false, "panels": [ @@ -38,8 +38,11 @@ }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -77,7 +80,7 @@ }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -89,33 +92,32 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.kubescore", - "rawQuery": "SELECT * FROM default.kubescore", + "query": "SELECT * FROM default.kubescore\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.kubescore\nWHERE EventTime >= toDateTime(1694245574) AND EventTime <= toDateTime(1694267174)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "KubeScore", - "transparent": true, "type": "table" } ], "refresh": "", - "schemaVersion": 35, + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "KubeScore", "uid": "d8f0fceb-7621-45bc-9710-89e11fe57a79", - "version": 2, + "version": 1, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index 9cbff0a2..592cb6ff 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, + "id": 8, "links": [], "liveNow": false, "panels": [ @@ -114,13 +114,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -190,13 +184,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Outdated Images", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -245,8 +233,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT count(*) FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", + "rawQuery": "SELECT count(*) FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694243264) AND EventTime <= toDateTime(1694243564) AND VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true @@ -266,13 +254,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -342,13 +324,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "DeletedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -397,8 +373,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeletedAPIs", - "rawQuery": "SELECT count(*) FROM default.DeletedAPIs", + "query": "SELECT count(*) FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694243293) AND EventTime <= toDateTime(1694243593)", "refId": "A", "round": "0s", "skip_comments": true @@ -418,13 +394,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "DeprecatedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -473,8 +443,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeprecatedAPIs", - "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs", + "query": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694243320) AND EventTime <= toDateTime(1694243620)", "refId": "A", "round": "0s", "skip_comments": true @@ -494,13 +464,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -549,8 +513,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.getall_resources", - "rawQuery": "SELECT count(*) FROM default.getall_resources", + "query": "SELECT count(*) FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694243344) AND EventTime <= toDateTime(1694243644)", "refId": "A", "round": "0s", "skip_comments": true @@ -694,13 +658,7 @@ "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -769,13 +727,7 @@ "description": "This panel displays the total number of pods with Created state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -846,13 +798,7 @@ "description": "This panel displays the total number of pods with backOff state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -923,13 +869,7 @@ "description": "This panel displays the total number of pods with Unhealthy state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1004,13 +944,7 @@ "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1079,13 +1013,7 @@ "description": "This panel displays the total number of nodes which is in not ready state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1156,13 +1084,7 @@ "description": "This panel displays the total number of nodes which is in ready state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1233,13 +1155,7 @@ "description": "This panel displays the total number of nodes which is in NodeHasNoDiskPressure state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1321,13 +1237,7 @@ "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1421,13 +1331,9 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total number of clusters containing activity uniquely.", + "description": "This panel displays the total number of clusters containing DeletedAPIs activity.", "fieldConfig": { "defaults": { - "color": { - "mode": "thresholds" - }, - "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1436,9 +1342,13 @@ "color": "green", "value": null }, + { + "color": "orange", + "value": 70 + }, { "color": "red", - "value": 80 + "value": 85 } ] } @@ -1446,12 +1356,12 @@ "overrides": [] }, "gridPos": { - "h": 5, - "w": 24, + "h": 4, + "w": 6, "x": 0, "y": 32 }, - "id": 40, + "id": 65, "options": { "orientation": "auto", "reduceOptions": { @@ -1481,7 +1391,61 @@ "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Clusters Containing DeletedAPIs Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the total number of clusters containing Events activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 32 + }, + "id": 67, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1491,14 +1455,67 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "refId": "B", + "query": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Clusters Containing Events Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the total number of clusters containing Outdated Images activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 32 + }, + "id": 68, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1508,14 +1525,67 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "refId": "C", + "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Clusters Containing Outdated_Images Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the total number of clusters containing DeprecatedAPIs activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 32 + }, + "id": 66, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1525,16 +1595,15 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "refId": "D", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Number of Clusters Containing Activity", + "title": "Clusters Containing DeprecatedAPIs Activity", "type": "gauge" }, { @@ -1556,19 +1625,14 @@ "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1600,7 +1664,7 @@ "h": 8, "w": 24, "x": 0, - "y": 37 + "y": 36 }, "id": 64, "options": { @@ -1627,8 +1691,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", - "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE EventTime >= toDateTime(1694243396) AND EventTime <= toDateTime(1694243696) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", "refId": "A", "round": "0s", "skip_comments": true @@ -1670,19 +1734,14 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "DeprecatedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1697,7 +1756,7 @@ "h": 9, "w": 12, "x": 0, - "y": 45 + "y": 44 }, "id": 34, "options": { @@ -1732,8 +1791,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694243425) AND EventTime <= toDateTime(1694243725)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1775,19 +1834,14 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "DeletedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1802,7 +1856,7 @@ "h": 9, "w": 12, "x": 12, - "y": 45 + "y": 44 }, "id": 36, "options": { @@ -1837,8 +1891,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694243449) AND EventTime <= toDateTime(1694243749)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1880,19 +1934,14 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Outdated Images", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1907,7 +1956,7 @@ "h": 8, "w": 8, "x": 0, - "y": 54 + "y": 53 }, "id": 28, "options": { @@ -1942,8 +1991,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694243473) AND EventTime <= toDateTime(1694243773)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1984,19 +2033,14 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2011,7 +2055,7 @@ "h": 8, "w": 8, "x": 8, - "y": 54 + "y": 53 }, "id": 32, "options": { @@ -2089,19 +2133,14 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2116,7 +2155,7 @@ "h": 8, "w": 8, "x": 16, - "y": 54 + "y": 53 }, "id": 30, "options": { @@ -2151,8 +2190,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694200604) AND EventTime <= toDateTime(1694243804)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2180,19 +2219,14 @@ "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "light-yellow", @@ -2228,7 +2262,7 @@ "h": 7, "w": 24, "x": 0, - "y": 62 + "y": 61 }, "id": 42, "options": { @@ -2255,8 +2289,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", - "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694200646) AND EventTime <= toDateTime(1694243846)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -2269,13 +2303,13 @@ "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 69 + "y": 68 }, "id": 16, "panels": [ @@ -2303,7 +2337,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2318,7 +2353,7 @@ "h": 16, "w": 24, "x": 0, - "y": 70 + "y": 69 }, "id": 14, "options": { @@ -2345,15 +2380,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.getall_resources", - "rawQuery": "SELECT * FROM default.getall_resources", + "query": "SELECT * FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694200677) AND EventTime <= toDateTime(1694243877)", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Kubernetes Resources", - "transparent": true, "type": "table" } ], @@ -2361,7 +2395,7 @@ { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "refId": "A" } @@ -2373,13 +2407,13 @@ "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 70 + "y": 69 }, "id": 12, "panels": [ @@ -2406,7 +2440,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2421,11 +2456,13 @@ "h": 16, "w": 24, "x": 0, - "y": 218 + "y": 70 }, "id": 10, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2434,7 +2471,7 @@ }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2446,15 +2483,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT * FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", + "rawQuery": "SELECT * FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694200706) AND EventTime <= toDateTime(1694243906) AND VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Outdated Images", - "transparent": true, "type": "table" } ], @@ -2462,7 +2498,7 @@ { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "refId": "A" } @@ -2474,13 +2510,13 @@ "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 71 + "y": 70 }, "id": 8, "panels": [ @@ -2508,7 +2544,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2523,11 +2560,13 @@ "h": 11, "w": 24, "x": 0, - "y": 219 + "y": 71 }, "id": 6, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2536,7 +2575,7 @@ }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2548,15 +2587,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeletedAPIs", - "rawQuery": "SELECT * FROM default.DeletedAPIs", + "query": "SELECT * FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694200743) AND EventTime <= toDateTime(1694243943)", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "DeletedAPIs", - "transparent": true, "type": "table" } ], @@ -2564,7 +2602,7 @@ { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "refId": "A" } @@ -2576,13 +2614,13 @@ "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 72 + "y": 71 }, "id": 4, "panels": [ @@ -2610,7 +2648,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2625,11 +2664,13 @@ "h": 8, "w": 24, "x": 0, - "y": 220 + "y": 72 }, "id": 2, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2638,7 +2679,7 @@ }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2650,15 +2691,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeprecatedAPIs", - "rawQuery": "SELECT * FROM default.DeprecatedAPIs", + "query": "SELECT * FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694200768) AND EventTime <= toDateTime(1694243968)", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "DeprecatedAPIs", - "transparent": true, "type": "table" } ], @@ -2666,7 +2706,7 @@ { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "vertamedia-clickhouse-datasource" }, "refId": "A" } @@ -2683,13 +2723,13 @@ "list": [] }, "time": { - "from": "now-30m", + "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 4, + "version": 3, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/kubvizFeatures-dashboard.json b/grafana/kubvizFeatures-dashboard.json index 79287253..90582557 100644 --- a/grafana/kubvizFeatures-dashboard.json +++ b/grafana/kubvizFeatures-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, + "id": 2, "links": [], "liveNow": false, "panels": [ @@ -92,8 +92,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.getall_resources", - "rawQuery": "SELECT * FROM default.getall_resources", + "query": "SELECT * FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694219529) AND EventTime <= toDateTime(1694241129)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -169,8 +169,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT * FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694219614) AND EventTime <= toDateTime(1694241214) AND VersionsBehind > 0\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -246,8 +246,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeletedAPIs", - "rawQuery": "SELECT * FROM default.DeletedAPIs", + "query": "SELECT * FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694219743) AND EventTime <= toDateTime(1694241343)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -323,8 +323,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeprecatedAPIs", - "rawQuery": "SELECT * FROM default.DeprecatedAPIs", + "query": "SELECT * FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694219780) AND EventTime <= toDateTime(1694241380)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -342,7 +342,7 @@ "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, @@ -351,4 +351,4 @@ "uid": "o2M7hbrVk", "version": 1, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index da050cba..45860fa3 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 4, + "id": 7, "links": [], "liveNow": false, "panels": [ @@ -85,8 +85,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nGROUP BY cluster_name, vul_severity", - "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nGROUP BY cluster_name, vul_severity", + "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, vul_severity", + "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155614) AND vul_last_modified_date <= toDateTime(1694242014)\nGROUP BY cluster_name, vul_severity", "refId": "A", "round": "0s", "skip_comments": true @@ -155,8 +155,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nGROUP BY cluster_name, misconfig_severity", - "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nGROUP BY cluster_name, misconfig_severity", + "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, misconfig_severity", + "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156239) AND EventTime <= toDateTime(1694242639)\nGROUP BY cluster_name, misconfig_severity", "refId": "A", "round": "0s", "skip_comments": true @@ -170,7 +170,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total of misconfigurations from each clusters.", + "description": "This panel displays the total number Vulnerabilities under each namespace", "fieldConfig": { "defaults": { "mappings": [], @@ -200,7 +200,7 @@ "x": 0, "y": 8 }, - "id": 16, + "id": 18, "options": { "orientation": "auto", "reduceOptions": { @@ -223,14 +223,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nGROUP BY cluster_name", + "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155665) AND vul_last_modified_date <= toDateTime(1694242065)\nGROUP BY cluster_name", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Count by Cluster", + "title": "Vulnerability Count by Cluster", "type": "gauge" }, { @@ -238,7 +238,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total number Vulnerabilities under each namespace", + "description": "This panel displays the total of misconfigurations from each clusters.", "fieldConfig": { "defaults": { "mappings": [], @@ -268,7 +268,7 @@ "x": 12, "y": 8 }, - "id": 18, + "id": 16, "options": { "orientation": "auto", "reduceOptions": { @@ -291,14 +291,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nGROUP BY cluster_name", + "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156266) AND EventTime <= toDateTime(1694242666)\nGROUP BY cluster_name", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Count by Cluster", + "title": "Misconfiguration Count by Cluster", "type": "gauge" }, { @@ -306,7 +306,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "description": "This panel provides a count of high vulnerabilities categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { @@ -335,6 +335,426 @@ "x": 0, "y": 13 }, + "id": 29, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155706) AND vul_last_modified_date <= toDateTime(1694242106) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "High Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of high misconfigurations categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 13 + }, + "id": 30, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156303) AND EventTime <= toDateTime(1694242703) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "High Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of Low vulnerabilities categorized by namespace. It helps to monitor and prioritize Low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 27, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155745) AND vul_last_modified_date <= toDateTime(1694242145) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Low Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of low misconfigurations categorized by namespace. It helps to monitor and prioritize low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 28, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156331) AND EventTime <= toDateTime(1694242731) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Low Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of Medium vulnerabilities categorized by namespace. It helps to monitor and prioritize Medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 25, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155776) AND vul_last_modified_date <= toDateTime(1694242176) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Medium Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of medium misconfigurations categorized by namespace. It helps to monitor and prioritize medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 26, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156357) AND EventTime <= toDateTime(1694242757) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Medium Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 34 + }, "id": 12, "options": { "displayMode": "basic", @@ -361,8 +781,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156016) AND vul_last_modified_date <= toDateTime(1694242416) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true @@ -403,7 +823,7 @@ "h": 7, "w": 12, "x": 12, - "y": 13 + "y": 34 }, "id": 14, "options": { @@ -432,8 +852,8 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "interval": "", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156383) AND EventTime <= toDateTime(1694242783) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true @@ -447,7 +867,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total count of Misconfiguration severity for each level", + "description": "This panel displays the total count of Vulnerability severity for each level", "fieldConfig": { "defaults": { "mappings": [], @@ -475,9 +895,9 @@ "h": 6, "w": 12, "x": 0, - "y": 20 + "y": 41 }, - "id": 8, + "id": 10, "options": { "orientation": "auto", "reduceOptions": { @@ -502,8 +922,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'HIGH'", - "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'HIGH'", + "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'", + "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true @@ -519,8 +939,8 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'MEDIUM'", - "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'MEDIUM'", + "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'", + "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'MEDIUM'", "refId": "B", "round": "0s", "skip_comments": true @@ -536,8 +956,8 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'LOW'", - "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'LOW'", + "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'", + "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'LOW'", "refId": "C", "round": "0s", "skip_comments": true @@ -553,14 +973,14 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'", - "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'", + "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'", + "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'CRITICAL'", "refId": "D", "round": "0s", "skip_comments": true } ], - "title": "Count of Misconfiguration Severity Level", + "title": "Count of Vulnereability Severity level", "type": "gauge" }, { @@ -568,7 +988,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total count of Vulnerability severity for each level", + "description": "This panel displays the total count of Misconfiguration severity for each level", "fieldConfig": { "defaults": { "mappings": [], @@ -596,9 +1016,9 @@ "h": 6, "w": 12, "x": 12, - "y": 20 + "y": 41 }, - "id": 10, + "id": 8, "options": { "orientation": "auto", "reduceOptions": { @@ -623,8 +1043,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'HIGH'", - "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'HIGH'", + "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'", + "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true @@ -640,8 +1060,8 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'MEDIUM'", - "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'MEDIUM'", + "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'", + "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'MEDIUM'", "refId": "B", "round": "0s", "skip_comments": true @@ -657,8 +1077,8 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'LOW'", - "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'LOW'", + "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'", + "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'LOW'", "refId": "C", "round": "0s", "skip_comments": true @@ -674,14 +1094,14 @@ "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'", - "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'", + "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'", + "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'CRITICAL'", "refId": "D", "round": "0s", "skip_comments": true } ], - "title": "Count of Vulnereability Severity level", + "title": "Count of Misconfiguration Severity Level", "type": "gauge" }, { @@ -689,7 +1109,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", + "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", "fieldConfig": { "defaults": { "color": { @@ -716,9 +1136,9 @@ "h": 8, "w": 12, "x": 0, - "y": 26 + "y": 47 }, - "id": 4, + "id": 6, "options": { "displayMode": "lcd", "minVizHeight": 10, @@ -744,14 +1164,14 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156175) AND vul_last_modified_date <= toDateTime(1694242575)\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Count by Cluster and Namespace", + "title": "Vulnerability Count by Cluster and Namespace", "type": "bargauge" }, { @@ -759,7 +1179,7 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", + "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", "fieldConfig": { "defaults": { "color": { @@ -786,9 +1206,9 @@ "h": 8, "w": 12, "x": 12, - "y": 26 + "y": 47 }, - "id": 6, + "id": 4, "options": { "displayMode": "lcd", "minVizHeight": 10, @@ -814,20 +1234,20 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156545) AND EventTime <= toDateTime(1694242945)\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Count by Cluster and Namespace", + "title": "Misconfiguration Count by Cluster and Namespace", "type": "bargauge" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "fieldConfig": { "defaults": { @@ -849,10 +1269,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -863,9 +1279,9 @@ "h": 8, "w": 24, "x": 0, - "y": 34 + "y": 55 }, - "id": 2, + "id": 32, "options": { "cellHeight": "sm", "footer": { @@ -881,40 +1297,20 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "cluster_name", - "namespace", - "kind", - "name", - "vul_id", - "vul_vendor_ids", - "vul_pkg_id", - "vul_pkg_name", - "vul_pkg_path", - "vul_installed_version", - "vul_fixed_version", - "vul_title", - "vul_severity", - "vul_published_date", - "vul_last_modified_date" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivy_vul" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" FROM \"default\".\"trivy_vul\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE vul_last_modified_date >= toDateTime(1694099993) AND vul_last_modified_date <= toDateTime(1694186393)\nORDER BY vul_last_modified_date DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy Vulnerabilities", @@ -922,10 +1318,9 @@ }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "description": "", "fieldConfig": { "defaults": { "color": { @@ -946,10 +1341,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -957,12 +1348,12 @@ "overrides": [] }, "gridPos": { - "h": 10, + "h": 8, "w": 24, "x": 0, - "y": 42 + "y": 63 }, - "id": 1, + "id": 33, "options": { "cellHeight": "sm", "footer": { @@ -978,39 +1369,20 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "cluster_name", - "namespace", - "kind", - "name", - "misconfig_id", - "misconfig_avdid", - "misconfig_type", - "misconfig_title", - "misconfig_desc", - "misconfig_msg", - "misconfig_query", - "misconfig_resolution", - "misconfig_severity", - "misconfig_status" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivy_misconfig" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" FROM \"default\".\"trivy_misconfig\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694156566) AND EventTime <= toDateTime(1694242966)", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy Misconfiguration", @@ -1018,8 +1390,8 @@ }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "fieldConfig": { "defaults": { @@ -1041,10 +1413,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -1052,12 +1420,12 @@ "overrides": [] }, "gridPos": { - "h": 10, + "h": 8, "w": 24, "x": 0, - "y": 52 + "y": 71 }, - "id": 24, + "id": 34, "options": { "cellHeight": "sm", "footer": { @@ -1073,79 +1441,20 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "0": "T", - "1": "h", - "2": "e", - "3": " ", - "4": "q", - "5": "u", - "6": "e", - "7": "r", - "8": "y", - "9": " ", - "10": "i", - "11": "s", - "12": " ", - "13": "n", - "14": "o", - "15": "t", - "16": " ", - "17": "a", - "18": " ", - "19": "s", - "20": "e", - "21": "l", - "22": "e", - "23": "c", - "24": "t", - "25": " ", - "26": "s", - "27": "t", - "28": "a", - "29": "t", - "30": "e", - "31": "m", - "32": "e", - "33": "n", - "34": "t", - "35": ".", - "database": "default", - "fields": [ - "cluster_name", - "artifact_name", - "vul_id", - "vul_pkg_id", - "vul_pkg_name", - "vul_installed_version", - "vul_fixed_version", - "vul_title", - "vul_severity", - "vul_published_date", - "vul_last_modified_date" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivyimage" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "queryType": "builder", - "rawSql": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" FROM \"default\".\"trivyimage\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy Image", @@ -1153,8 +1462,8 @@ }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, "fieldConfig": { "defaults": { @@ -1186,9 +1495,9 @@ "h": 8, "w": 24, "x": 0, - "y": 62 + "y": 79 }, - "id": 25, + "id": 35, "options": { "cellHeight": "sm", "footer": { @@ -1204,26 +1513,20 @@ "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "*" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivysbom" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT * FROM \"default\".\"trivysbom\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE $timeFilterByColumn(metadata_timestamp)\nORDER BY metadata_timestamp DESC", + "rawQuery": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE metadata_timestamp >= toDateTime(1693581733) AND metadata_timestamp <= toDateTime(1694186533)\nORDER BY metadata_timestamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy_SBOM", @@ -1238,13 +1541,13 @@ "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Trivy", - "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d47", - "version": 1, + "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", + "version": 2, "weekStart": "" -} +} \ No newline at end of file From 13c1542dcb152f7229f7f1a1dd83b9cfce6bee3f Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sun, 10 Sep 2023 14:52:21 +0530 Subject: [PATCH 027/263] added liveness and pprof --- agent/kubviz/k8smetrics_agent.go | 3 ++- agent/server/server.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 agent/server/server.go diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index c58bf021..f86d163f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -29,6 +29,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" + "github.com/intelops/kubviz/agent/server" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" ) @@ -104,7 +105,7 @@ func main() { clientset = getK8sClient(config) } go publishMetrics(clientset, js, clusterMetricsChan) - + go server.StartServer() collectAndPublishMetrics := func() { err := outDatedImages(config, js) LogErr(err) diff --git a/agent/server/server.go b/agent/server/server.go new file mode 100644 index 00000000..ebb3d129 --- /dev/null +++ b/agent/server/server.go @@ -0,0 +1,30 @@ +package server + +import ( + "log" + "net/http" + _ "net/http/pprof" + + "github.com/gin-gonic/gin" +) + +func EnableProfile(r *gin.Engine) { + pprofGroup := r.Group("/debug/pprof") + { + pprofGroup.GET("/", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/cmdline", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/profile", gin.WrapH(http.DefaultServeMux)) + pprofGroup.POST("/symbol", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/symbol", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/trace", gin.WrapH(http.DefaultServeMux)) + } + r.GET("/liveness", func(c *gin.Context) { + c.String(http.StatusOK, "Alive") + }) +} + +func StartServer() { + r := gin.Default() + EnableProfile(r) + log.Fatal(r.Run(":6060")) +} From b5ee77ebd4f454a6ffa26d5bc43ddbacec103725 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sun, 10 Sep 2023 15:09:40 +0530 Subject: [PATCH 028/263] minor fix --- agent/server/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/server/server.go b/agent/server/server.go index ebb3d129..1f02fc61 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -26,5 +26,5 @@ func EnableProfile(r *gin.Engine) { func StartServer() { r := gin.Default() EnableProfile(r) - log.Fatal(r.Run(":6060")) + log.Fatal(r.Run(":8080")) } From 8e5ebdb988cda9e568090fcd4d3c0598d8d660ac Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sun, 10 Sep 2023 15:23:42 +0530 Subject: [PATCH 029/263] minor fix --- agent/server/server.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/agent/server/server.go b/agent/server/server.go index 1f02fc61..cee938ae 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -17,7 +17,14 @@ func EnableProfile(r *gin.Engine) { pprofGroup.POST("/symbol", gin.WrapH(http.DefaultServeMux)) pprofGroup.GET("/symbol", gin.WrapH(http.DefaultServeMux)) pprofGroup.GET("/trace", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/allocs", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/block", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/goroutine", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/heap", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/mutex", gin.WrapH(http.DefaultServeMux)) + pprofGroup.GET("/threadcreate", gin.WrapH(http.DefaultServeMux)) } + r.GET("/liveness", func(c *gin.Context) { c.String(http.StatusOK, "Alive") }) From 084553a360f01e3d84384a25d610ac68720923bd Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Mon, 11 Sep 2023 18:09:49 +0530 Subject: [PATCH 030/263] customized the default time to 24h --- grafana/kubvizDsahboard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index 592cb6ff..cc4b8a44 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -2723,7 +2723,7 @@ "list": [] }, "time": { - "from": "now-5m", + "from": "now-24h", "to": "now" }, "timepicker": {}, From 9ddb0ed66c0913213b1fd8438cd87a30c9af08eb Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 12 Sep 2023 14:36:52 +0530 Subject: [PATCH 031/263] migration added --- .github/workflows/migration-image.yml | 79 +++++++ .github/workflows/migration-pr.yml | 47 ++++ .github/workflows/migration-release.yml | 60 ++++++ .gitignore | 5 +- charts/client/Chart.yaml | 2 +- charts/client/templates/deployment.yaml | 16 ++ charts/client/values.yaml | 11 +- client/pkg/clickhouse/db_client.go | 13 +- client/pkg/clickhouse/statements.go | 3 +- client/pkg/clients/bridge_client.go | 30 +-- cmd/cli/commands/sql.go | 83 ++++++++ cmd/cli/config/config.go | 34 +++ cmd/cli/main.go | 7 + dockerfiles/migration/Dockerfile | 22 ++ go.mod | 6 +- go.sum | 10 +- model/gitbridge.go | 2 +- script/wait-for-clickhouse.sh | 21 ++ sql/20230912051648_clickhouse.down.sql | 20 ++ sql/20230912051648_clickhouse.up.sql | 272 ++++++++++++++++++++++++ 20 files changed, 711 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/migration-image.yml create mode 100644 .github/workflows/migration-pr.yml create mode 100644 .github/workflows/migration-release.yml create mode 100644 cmd/cli/commands/sql.go create mode 100644 cmd/cli/config/config.go create mode 100644 cmd/cli/main.go create mode 100644 dockerfiles/migration/Dockerfile create mode 100644 script/wait-for-clickhouse.sh create mode 100644 sql/20230912051648_clickhouse.down.sql create mode 100644 sql/20230912051648_clickhouse.up.sql diff --git a/.github/workflows/migration-image.yml b/.github/workflows/migration-image.yml new file mode 100644 index 00000000..52772354 --- /dev/null +++ b/.github/workflows/migration-image.yml @@ -0,0 +1,79 @@ +name: Migration Docker Image CI + +on: + push: + paths-ignore: + - '**.md' + branches: + - 'main' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + packages: write + id-token: write + contents: read + actions: read + security-events: write + env: + REGISTRY: ghcr.io + GH_URL: https://github.com + steps: + - name: Checkout GitHub Action + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v2 + + - name: Docker metadata + id: metadata + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ github.repository }}/migration + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }} + flavor: | + latest=true + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build image and push to GitHub Container Registry + uses: docker/build-push-action@v4 + with: + context: . + file: ./dockerfiles/migration/Dockerfile + tags: ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ github.run_id }} + labels: ${{ steps.metadata.outputs.labels }} + push: true + + - name: Install cosign + uses: sigstore/cosign-installer@main + + - name: Sign the images + run: | + cosign sign -y ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ github.run_id }} + env: + COSIGN_EXPERIMENTAL: 1 + + - name: Verify the pushed tags + run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/migration.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com + env: + COSIGN_EXPERIMENTAL: 1 + + - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + format: 'github' + output: 'dependency-results.sbom.json' + image-ref: '.' + github-pat: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/migration-pr.yml b/.github/workflows/migration-pr.yml new file mode 100644 index 00000000..3d582691 --- /dev/null +++ b/.github/workflows/migration-pr.yml @@ -0,0 +1,47 @@ +name: Migration Docker Image CI + +on: + pull_request: + branches: + - 'main' + +jobs: + build: + runs-on: ubuntu-latest + env: + REGISTRY: ghcr.io + GH_URL: https://github.com + steps: + - + name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - + name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - + name: Login to ghcr registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - + name: Build and push on PR + uses: docker/build-push-action@v4 + if: github.event_name == 'pull_request' + with: + context: . + file: ./dockerfiles/migration/Dockerfile + push: true + tags: ${{ env.REGISTRY }}/${{ github.repository }}/migration:pr-${{ github.event.pull_request.number }} + build-args: | + "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/migration-release.yml b/.github/workflows/migration-release.yml new file mode 100644 index 00000000..f50446b3 --- /dev/null +++ b/.github/workflows/migration-release.yml @@ -0,0 +1,60 @@ +name: migration-release + +on: + push: + tags: + - "v*.*.*" + +jobs: + push_to_registry: + name: Build and push Docker image to GitHub container registry. + runs-on: ubuntu-20.04 + permissions: + packages: write + id-token: write + contents: read + actions: read + security-events: write + env: + REGISTRY: ghcr.io + GH_URL: https://github.com + steps: + - name: Set environment variable + run: | + echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV + - name: Test environment variable + run: echo ${{ env.RELEASE_VERSION }} + - name: Check out GitHub repo + uses: actions/checkout@v3 + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build image and push to GitHub Container Registry + uses: docker/build-push-action@v4 + with: + push: true + context: ./ + file: ./dockerfiles/migration/Dockerfile + tags: ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ env.RELEASE_VERSION }} + - name: Install cosign + uses: sigstore/cosign-installer@main + - name: Sign the images + run: | + cosign sign -y ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ env.RELEASE_VERSION }} + env: + COSIGN_EXPERIMENTAL: 1 + - name: Verify the pushed tags + run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ env.RELEASE_VERSION }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/migration-release.yml@refs/tags/${{ env.RELEASE_VERSION }} --certificate-oidc-issuer https://token.actions.githubusercontent.com + env: + COSIGN_EXPERIMENTAL: 1 + - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + format: 'github' + output: 'dependency-results.sbom.json' + image-ref: '.' + github-pat: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 8bb71814..87c9216c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ civo -migration/ +alloc.svg +allocs.pprof +cpu.pprof +steps-to-test.txt diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 7caa1d5b..79ef4335 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -26,7 +26,7 @@ dependencies: - name: nats condition: nats.enabled version: 0.13.4 - repository: https://intelops.github.io/kubviz/ + repository: https://intelops.github.io/kubviz/ - name: clickhouse condition: clickhouse.enabled version: 1.0.2 diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index 7dd1c34b..0fd4d7fd 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -27,6 +27,22 @@ spec: serviceAccountName: {{ include "client.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + - name: migration-init + image: "{{ .Values.migration.image.repository }}:{{ .Values.migration.image.tag }}" + imagePullPolicy: {{ .Values.migration.image.pullPolicy }} + command: + - /bin/sh + - -c + args: + - "/script/wait-for-clickhouse.sh && /migration sql -e --yes" + env: + - name: SCHEMA_PATH + value : {{ .Values.migration.schema.path }} + - name: DB_ADDRESS + value: {{ include "client.fullname" . }}-clickhouse + - name: DB_PORT + value: "9000" containers: - name: {{ .Chart.Name }} securityContext: diff --git a/charts/client/values.yaml b/charts/client/values.yaml index ee6c23d7..a4053a25 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -1,4 +1,4 @@ -# Default values for client. +# Default values for client. # This is a YAML-formatted file. # Declare variables to be passed into your templates. @@ -106,3 +106,12 @@ datasources: label: grafana_datasource labelValue: "1" uid: vertamedia-clickhouse-datasource + +migration: + enabled: true + image: + repository: ghcr.io/intelops/kubviz/migration + pullPolicy: Always + tag: "v1.1.0" + schema: + path: "/sql" diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index c613c4b1..e14ed053 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -74,12 +74,12 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { return nil, err } - tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, quayContainerPushEventTable, jfrogContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} - for _, table := range tables { - if err = splconn.Exec(context.Background(), string(table)); err != nil { - return nil, err - } - } + // tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, quayContainerPushEventTable, jfrogContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} + // for _, table := range tables { + // if err = splconn.Exec(context.Background(), string(table)); err != nil { + // return nil, err + // } + // } stdconn := clickhouse.OpenDB(&clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, }) @@ -126,7 +126,6 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE tag, imageName, string(pushEventJSON), - pushEvent.Timestamp, size, shaID, currentTime, diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index eef44eb5..8d2bb614 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -183,7 +183,6 @@ const azureContainerPushEventTable DBStatement = ` Tag String, ImageName String, Event String, - Timestamp String, Size Int32, SHAID String, EventTime DateTime('UTC') @@ -242,7 +241,7 @@ const InsertKubeScore string = "INSERT INTO kubescore (id, namespace, cluster_na const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?)" const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Timestamp, Size, SHAID, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivySbom string = "INSERT INTO trivysbom (id, schema, bom_format,spec_version,serial_number, version, metadata_timestamp,metatool_vendor,metatool_name,metatool_version,component_bom_ref,component_type,component_name,component_version,component_property_name,component_property_value,component_hash_alg,component_hash_content,component_license_exp,component_purl,dependency_ref) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/client/pkg/clients/bridge_client.go b/client/pkg/clients/bridge_client.go index 87f93014..3082bf57 100644 --- a/client/pkg/clients/bridge_client.go +++ b/client/pkg/clients/bridge_client.go @@ -89,7 +89,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.Resource.Repository.RemoteURL + "/commit/" + pl.Resource.RefUpdates[0].NewObjectID gca.EventType = string(azuremodel.GitPushEventType) gca.RepoName = pl.Resource.Repository.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertAzureDevops) log.Println("Inserted AzureDevops metrics:", string(msg.Data)) @@ -108,7 +108,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.Resource.Repository.RemoteURL + "/commit/" + pl.Resource.LastMergeCommit.CommitID gca.EventType = string(azuremodel.GitPullRequestMergedEventType) gca.RepoName = pl.Resource.Repository.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertAzureDevops) log.Println("Inserted AzureDevops metrics:", string(msg.Data)) @@ -121,7 +121,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitID = "" gca.CommitUrl = "" gca.EventType = event - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertAzureDevops) log.Println("Inserted GitHub metrics:", string(msg.Data)) @@ -155,7 +155,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { } gca.EventType = string(github.PushEvent) gca.RepoName = pl.Repository.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGithub) log.Println("Inserted GitHub metrics:", string(msg.Data)) @@ -179,7 +179,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.PullRequest.HTMLURL gca.EventType = string(github.PullRequestEvent) gca.RepoName = pl.Repository.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGithub) log.Println("Inserted GitHub metrics:", string(msg.Data)) @@ -193,7 +193,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitID = "" gca.CommitUrl = "" gca.EventType = event - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGithub) log.Println("Inserted GitHub metrics:", string(msg.Data)) @@ -219,7 +219,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.CompareURL gca.EventType = string(gitea.PushEvent) gca.RepoName = pl.Repo.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGitea) log.Println("Inserted Gitea metrics:", string(msg.Data)) @@ -253,7 +253,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { } else { gca.RepoName = "" } - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGitea) log.Println("Inserted Gitea metrics:", string(msg.Data)) @@ -265,7 +265,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitID = "" gca.CommitUrl = "" gca.EventType = event - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.RepoName = "" gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGitea) @@ -292,7 +292,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.Project.WebURL + "/commit/" + pl.After gca.EventType = string(gitlab.PushEvents) gca.RepoName = pl.Project.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGitlab) log.Println("Inserted Gitlab metrics:", string(msg.Data)) @@ -312,7 +312,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.ObjectAttributes.URL gca.EventType = string(gitlab.MergeRequestEvents) gca.RepoName = pl.Project.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGitlab) log.Println("Inserted Gitlab metrics:", string(msg.Data)) @@ -325,7 +325,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitID = "" gca.CommitUrl = "" gca.EventType = event - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.RepoName = "" gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertGitlab) @@ -353,7 +353,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { } gca.EventType = string(bitbucket.RepoPushEvent) gca.RepoName = pl.Repository.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertBitbucket) log.Println("Inserted BitBucket metrics:", string(msg.Data)) @@ -372,7 +372,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitUrl = pl.PullRequest.Links.HTML.Href gca.EventType = string(bitbucket.PullRequestMergedEvent) gca.RepoName = pl.Repository.Name - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertBitbucket) log.Println("Inserted BitBucket metrics:", string(msg.Data)) @@ -384,7 +384,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { gca.CommitID = "" gca.CommitUrl = "" gca.EventType = event - gca.TimeStamp = time.Now().Format(time.DateTime) + gca.TimeStamp = time.Now().UTC() gca.RepoName = "" gca.Event = string(msg.Data) conn.InsertGitCommon(gca, dbstatement.InsertBitbucket) diff --git a/cmd/cli/commands/sql.go b/cmd/cli/commands/sql.go new file mode 100644 index 00000000..a615ce1c --- /dev/null +++ b/cmd/cli/commands/sql.go @@ -0,0 +1,83 @@ +package commands + +import ( + "fmt" + "log" + "os" + + "github.com/golang-migrate/migrate/v4" + cm "github.com/golang-migrate/migrate/v4/database/clickhouse" + "github.com/intelops/kubviz/cmd/cli/config" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "migration", + Short: "CLI for managing migrations", + Long: `A CLI tool developed to manage migrations for Kubviz Client ClickHouse.`, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +var sqlCmd = &cobra.Command{ + Use: "sql", + Short: "Manage SQL migrations", + Long: `The sql subcommand is used to manage SQL migrations for Kubviz Client ClickHouse. +You can execute migrations using the -e flag and confirm with --yes.`, + Example: ` +# Execute migrations and confirm +migration sql -e --yes + +# Execute migrations without confirmation +migration sql -e --no`, + Run: func(cmd *cobra.Command, args []string) { + executeFlag, _ := cmd.Flags().GetBool("execute") + yesFlag, _ := cmd.Flags().GetBool("yes") + + if !executeFlag && !yesFlag { + cmd.Help() + return + } + + if executeFlag { + if yesFlag { + db, cfg, err := config.OpenClickHouseConn() + if err != nil { + log.Fatalf("Failed to open ClickHouse connection: %v", err) + } + defer db.Close() + driver, err := cm.WithInstance(db, &cm.Config{}) + if err != nil { + log.Fatalf("Failed to create migrate driver: %v", err) + } + + m, err := migrate.NewWithDatabaseInstance( + fmt.Sprintf("file://%s", cfg.SchemaPath), + "clickhouse", + driver, + ) + if err != nil { + log.Fatalf("Clickhouse Migration initialization failed: %v", err) + } + if err := m.Up(); err != nil && err != migrate.ErrNoChange { + log.Fatalf("Migration failed: %v", err) + } + fmt.Println("Clickhouse Migrations applied successfully!") + } else { + fmt.Println("Clickhouse Migration skipped due to --no flag.") + } + } + }, +} + +func init() { + sqlCmd.Flags().BoolP("execute", "e", false, "Execute the migrations") + sqlCmd.Flags().BoolP("yes", "y", false, "Confirm execution") + + rootCmd.AddCommand(sqlCmd) +} diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go new file mode 100644 index 00000000..394097c2 --- /dev/null +++ b/cmd/cli/config/config.go @@ -0,0 +1,34 @@ +package config + +import ( + "database/sql" + "fmt" + + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/kelseyhightower/envconfig" +) + +type Config struct { + DbPort int `envconfig:"DB_PORT" required:"true"` + DBAddress string `envconfig:"DB_ADDRESS" required:"true"` + SchemaPath string `envconfig:"SCHEMA_PATH" default:"/sql"` +} + +func OpenClickHouseConn() (*sql.DB, *Config, error) { + var cfg Config + err := envconfig.Process("", &cfg) + if err != nil { + return nil, nil, err + } + conn := clickhouse.OpenDB(&clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + }) + if err := conn.Ping(); err != nil { + if exception, ok := err.(*clickhouse.Exception); ok { + return nil, nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) + } else { + return nil, nil, err + } + } + return conn, &cfg, nil +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 00000000..2c8f6fed --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/intelops/kubviz/cmd/cli/commands" + +func main() { + commands.Execute() +} diff --git a/dockerfiles/migration/Dockerfile b/dockerfiles/migration/Dockerfile new file mode 100644 index 00000000..dbd4949e --- /dev/null +++ b/dockerfiles/migration/Dockerfile @@ -0,0 +1,22 @@ +# Build the manager binary +FROM golang:1.20 as builder + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY ./ ./ +RUN go mod download + +# Build +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o migration cmd/cli/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/migration . +COPY /workspace/sql /sql +COPY /workspace/script /script +RUN chmod +x /script/wait-for-clickhouse.sh +USER 65532:65532 + +ENTRYPOINT ["/migration"] diff --git a/go.mod b/go.mod index f59496e8..414c54bb 100644 --- a/go.mod +++ b/go.mod @@ -11,16 +11,17 @@ require ( github.com/docker/docker v24.0.4+incompatible github.com/genuinetools/reg v0.16.1 github.com/getkin/kin-openapi v0.118.0 - github.com/ghodss/yaml v1.0.0 github.com/gin-gonic/gin v1.9.1 github.com/go-co-op/gocron v1.30.1 github.com/go-playground/webhooks/v6 v6.2.0 + github.com/golang-migrate/migrate/v4 v4.16.2 github.com/google/uuid v1.3.0 github.com/hashicorp/go-version v1.6.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/nats-io/nats.go v1.27.1 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.7.0 golang.org/x/term v0.10.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 @@ -70,6 +71,8 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.1.0 // indirect @@ -110,7 +113,6 @@ require ( github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spdx/tools-golang v0.5.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/testify v1.8.4 // indirect diff --git a/go.sum b/go.sum index 1b905253..1570851a 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,7 @@ cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/ClickHouse/ch-go v0.52.1 h1:nucdgfD1BDSHjbNaG3VNebonxJzD8fX8jbuBpfo5VY0= @@ -178,6 +178,8 @@ github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355/go.mod h1:cY github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA= +github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -228,10 +230,13 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.11.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= @@ -338,6 +343,7 @@ github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5s github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/open-policy-agent/opa v0.45.0 h1:P5nuhVRtR+e58fk3CMMbiqr6ZFyWQPNOC3otsorGsFs= @@ -555,7 +561,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= 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= diff --git a/model/gitbridge.go b/model/gitbridge.go index 950af4b2..d69131e4 100644 --- a/model/gitbridge.go +++ b/model/gitbridge.go @@ -40,6 +40,6 @@ type GitCommonAttribute struct { CommitUrl string EventType string RepoName string - TimeStamp string + TimeStamp time.Time Event string } diff --git a/script/wait-for-clickhouse.sh b/script/wait-for-clickhouse.sh new file mode 100644 index 00000000..26555ec3 --- /dev/null +++ b/script/wait-for-clickhouse.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +CLICKHOUSE_HOST="${DB_ADDRESS}" +CLICKHOUSE_PORT="${DB_PORT}" +RETRY_INTERVAL=5 +MAX_RETRIES=60 + +retry_count=0 +while [ $retry_count -lt $MAX_RETRIES ]; do + if clickhouse-client --host $CLICKHOUSE_HOST --port $CLICKHOUSE_PORT --query "SELECT 1"; then + echo "ClickHouse is ready!" + exit 0 + else + echo "Failed to connect to ClickHouse. Retrying in $RETRY_INTERVAL seconds..." + retry_count=$((retry_count + 1)) + sleep $RETRY_INTERVAL + fi +done + +echo "Failed to connect to ClickHouse after $MAX_RETRIES retries. Exiting." +exit 1 diff --git a/sql/20230912051648_clickhouse.down.sql b/sql/20230912051648_clickhouse.down.sql new file mode 100644 index 00000000..5cbad299 --- /dev/null +++ b/sql/20230912051648_clickhouse.down.sql @@ -0,0 +1,20 @@ +DROP TABLE IF EXISTS events; +DROP TABLE IF EXISTS rakkess; +DROP TABLE IF EXISTS DeprecatedAPIs; +DROP TABLE IF EXISTS DeletedAPIs; +DROP TABLE IF EXISTS jfrogcontainerpush; +DROP TABLE IF EXISTS getall_resources; +DROP TABLE IF EXISTS outdated_images; +DROP TABLE IF EXISTS kubescore; +DROP TABLE IF EXISTS trivy_vul; +DROP TABLE IF EXISTS trivy_misconfig; +DROP TABLE IF EXISTS trivyimage; +DROP TABLE IF EXISTS dockerhubbuild; +DROP TABLE IF EXISTS azurecontainerpush; +DROP TABLE IF EXISTS quaycontainerpush; +DROP TABLE IF EXISTS trivysbom; +DROP TABLE IF EXISTS azure_devops; +DROP TABLE IF EXISTS github; +DROP TABLE IF EXISTS gitlab; +DROP TABLE IF EXISTS bitbucket; +DROP TABLE IF EXISTS gitea; diff --git a/sql/20230912051648_clickhouse.up.sql b/sql/20230912051648_clickhouse.up.sql new file mode 100644 index 00000000..a53ee9a9 --- /dev/null +++ b/sql/20230912051648_clickhouse.up.sql @@ -0,0 +1,272 @@ +-- kubvizTable +CREATE TABLE IF NOT EXISTS events ( + ClusterName String, + Id String, + EventTime DateTime('UTC'), + OpType String, + Name String, + Namespace String, + Kind String, + Message String, + Reason String, + Host String, + Event String, + FirstTime String, + LastTime String +) engine=File(TabSeparated); + +-- rakeesTable +CREATE TABLE IF NOT EXISTS rakkess ( + ClusterName String, + Name String, + Create String, + Delete String, + List String, + Update String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- kubePugDepricatedTable +CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( + ClusterName String, + ObjectName String, + Description String, + Kind String, + Deprecated UInt8, + Scope String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- kubepugDeletedTable +CREATE TABLE IF NOT EXISTS DeletedAPIs ( + ClusterName String, + ObjectName String, + Group String, + Kind String, + Version String, + Name String, + Deleted UInt8, + Scope String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- jfrogContainerPushEventTable +CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( + Domain String, + EventType String, + RegistryURL String, + RepositoryName String, + SHAID String, + Size Int32, + ImageName String, + Tag String, + Event String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- ketallTable +CREATE TABLE IF NOT EXISTS getall_resources ( + ClusterName String, + Namespace String, + Kind String, + Resource String, + Age String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- outdateTable +CREATE TABLE IF NOT EXISTS outdated_images ( + ClusterName String, + Namespace String, + Pod String, + CurrentImage String, + CurrentTag String, + LatestVersion String, + VersionsBehind Int64, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- kubescoreTable +CREATE TABLE IF NOT EXISTS kubescore ( + id UUID, + namespace String, + cluster_name String, + recommendations String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- trivyTableVul +CREATE TABLE IF NOT EXISTS trivy_vul ( + id UUID, + cluster_name String, + namespace String, + kind String, + name String, + vul_id String, + vul_vendor_ids String, + vul_pkg_id String, + vul_pkg_name String, + vul_pkg_path String, + vul_installed_version String, + vul_fixed_version String, + vul_title String, + vul_severity String, + vul_published_date DateTime('UTC'), + vul_last_modified_date DateTime('UTC') +) engine=File(TabSeparated); + +-- trivyTableMisconfig +CREATE TABLE IF NOT EXISTS trivy_misconfig ( + id UUID, + cluster_name String, + namespace String, + kind String, + name String, + misconfig_id String, + misconfig_avdid String, + misconfig_type String, + misconfig_title String, + misconfig_desc String, + misconfig_msg String, + misconfig_query String, + misconfig_resolution String, + misconfig_severity String, + misconfig_status String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- trivyTableImage +CREATE TABLE IF NOT EXISTS trivyimage ( + id UUID, + cluster_name String, + artifact_name String, + vul_id String, + vul_pkg_id String, + vul_pkg_name String, + vul_installed_version String, + vul_fixed_version String, + vul_title String, + vul_severity String, + vul_published_date DateTime('UTC'), + vul_last_modified_date DateTime('UTC') +) engine=File(TabSeparated); + +-- dockerHubBuildTable +CREATE TABLE IF NOT EXISTS dockerhubbuild ( + PushedBy String, + ImageTag String, + RepositoryName String, + DateCreated String, + Owner String, + Event String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- azureContainerPushEventTable +CREATE TABLE IF NOT EXISTS azurecontainerpush ( + RegistryURL String, + RepositoryName String, + Tag String, + ImageName String, + Event String, + Size Int32, + SHAID String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- quayContainerPushEventTable +CREATE TABLE IF NOT EXISTS quaycontainerpush ( + name String, + repository String, + nameSpace String, + dockerURL String, + homePage String, + tag String, + Event String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); + +-- trivySbomTable +CREATE TABLE IF NOT EXISTS trivysbom ( + id UUID, + schema String, + bom_format String, + spec_version String, + serial_number String, + version INTEGER, + metadata_timestamp DateTime('UTC'), + metatool_vendor String, + metatool_name String, + metatool_version String, + component_bom_ref String, + component_type String, + component_name String, + component_version String, + component_property_name String, + component_property_value String, + component_hash_alg String, + component_hash_content String, + component_license_exp String, + component_purl String, + dependency_ref String +) engine=File(TabSeparated); + +-- AzureDevopsTable +CREATE TABLE IF NOT EXISTS azure_devops ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); + +-- GithubTable +CREATE TABLE IF NOT EXISTS github ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); + +-- GitlabTable +CREATE TABLE IF NOT EXISTS gitlab ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); + +-- BitbucketTable +CREATE TABLE IF NOT EXISTS bitbucket ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); + +-- GiteaTable +CREATE TABLE IF NOT EXISTS gitea ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); From 10e434f7c3263780a0a448b69308714de07b2973 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 10:17:50 +0530 Subject: [PATCH 032/263] minor fix --- dockerfiles/migration/Dockerfile | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dockerfiles/migration/Dockerfile b/dockerfiles/migration/Dockerfile index dbd4949e..6de44b79 100644 --- a/dockerfiles/migration/Dockerfile +++ b/dockerfiles/migration/Dockerfile @@ -20,3 +20,26 @@ RUN chmod +x /script/wait-for-clickhouse.sh USER 65532:65532 ENTRYPOINT ["/migration"] + + +FROM golang:1.20 as builder + +WORKDIR /workspace +COPY ./ ./ +RUN go mod download + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o migration cmd/cli/main.go + +RUN chmod +x /workspace/script/wait-for-clickhouse.sh + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/migration . +COPY --from=builder /workspace/sql /sql +COPY --from=builder /workspace/script /script + +USER 65532:65532 + +ENTRYPOINT ["/migration"] From b28c578950002b2fa29c75d3b469ef1d6e8938fd Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 10:24:12 +0530 Subject: [PATCH 033/263] client chart version changed --- charts/client/Chart.yaml | 2 +- dockerfiles/migration/Dockerfile | 24 ------------------------ 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 79ef4335..53bf6cf1 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.0 +version: 1.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/dockerfiles/migration/Dockerfile b/dockerfiles/migration/Dockerfile index 6de44b79..57d90bfc 100644 --- a/dockerfiles/migration/Dockerfile +++ b/dockerfiles/migration/Dockerfile @@ -1,27 +1,3 @@ -# Build the manager binary -FROM golang:1.20 as builder - -WORKDIR /workspace -# Copy the Go Modules manifests -COPY ./ ./ -RUN go mod download - -# Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o migration cmd/cli/main.go - -# Use distroless as minimal base image to package the manager binary -# Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot -WORKDIR / -COPY --from=builder /workspace/migration . -COPY /workspace/sql /sql -COPY /workspace/script /script -RUN chmod +x /script/wait-for-clickhouse.sh -USER 65532:65532 - -ENTRYPOINT ["/migration"] - - FROM golang:1.20 as builder WORKDIR /workspace From b05e0a1c2c9cde8375a8649f22d2fec9682012ff Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 10:36:02 +0530 Subject: [PATCH 034/263] minor fix in workflow --- .github/workflows/migration-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/migration-image.yml b/.github/workflows/migration-image.yml index 52772354..624594e6 100644 --- a/.github/workflows/migration-image.yml +++ b/.github/workflows/migration-image.yml @@ -65,7 +65,7 @@ jobs: COSIGN_EXPERIMENTAL: 1 - name: Verify the pushed tags - run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/migration.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com + run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/migration:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/migration-image.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com env: COSIGN_EXPERIMENTAL: 1 From c37d23be1dbd90d0362e8acdde5b320fc9295881 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 15 Sep 2023 10:59:36 +0530 Subject: [PATCH 035/263] Added Schedule --- agent/config/config.go | 11 ++- agent/kubviz/k8smetrics_agent.go | 105 +++++++++++++++++++++-- agent/kubviz/scheduler.go | 88 ++++++++++++++++++++ agent/kubviz/scheduler_watch.go | 138 +++++++++++++++++++++++++++++++ agent/kubviz/trivy.go | 35 +++++++- go.mod | 23 ++++-- go.sum | 58 ++++++++----- 7 files changed, 417 insertions(+), 41 deletions(-) create mode 100644 agent/kubviz/scheduler.go create mode 100644 agent/kubviz/scheduler_watch.go diff --git a/agent/config/config.go b/agent/config/config.go index 4fbe3658..50209ccd 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -6,8 +6,15 @@ import ( ) type AgentConfigurations struct { - SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` - SAName string `envconfig:"SA_NAME" default:"default"` + SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` + SAName string `envconfig:"SA_NAME" default:"default"` + OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"*/20 * * * *"` + GetAllInterval string `envconfig:"GETALL_INTERVAL" default:"*/30 * * * *"` + KubeScoreInterval string `envconfig:"KUBESCORE_INTERVAL" default:"*/40 * * * *"` + RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` + KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` + TrivyInterval string `envconfig:"TRIVY_INTERVAL" default:"*/10 * * * *"` + SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"false"` } func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index f86d163f..a807bac2 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -4,10 +4,14 @@ import ( "encoding/json" "log" "os" + "os/signal" "strconv" "strings" + "syscall" "time" + "github.com/intelops/go-common/logging" + "github.com/go-co-op/gocron" "github.com/nats-io/nats.go" @@ -22,6 +26,7 @@ import ( "fmt" + "github.com/intelops/kubviz/agent/config" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/fields" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" @@ -77,6 +82,10 @@ func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production clusterMetricsChan := make(chan error, 1) + cfg, err := config.GetAgentConfigurations() + if err != nil { + log.Fatal("Failed to retrieve agent configurations", err) + } var ( config *rest.Config clientset *kubernetes.Clientset @@ -126,15 +135,30 @@ func main() { if schedulingIntervalStr == "" { schedulingIntervalStr = "20m" } - schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) - if err != nil { - log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) + if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. + scheduler := initScheduler(config, js, *cfg, clientset) + + // Start the scheduler + scheduler.Start() + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + <-signals + + scheduler.Stop() + } else { + if schedulingIntervalStr == "" { + schedulingIntervalStr = "20m" + } + schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) + if err != nil { + log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) + } + s := gocron.NewScheduler(time.UTC) + s.Every(schedulingInterval).Do(func() { + collectAndPublishMetrics() + }) + s.StartBlocking() } - s := gocron.NewScheduler(time.UTC) - s.Every(schedulingInterval).Do(func() { - collectAndPublishMetrics() - }) - s.StartBlocking() } // publishMetrics publishes stream of events @@ -272,3 +296,68 @@ func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { time.Sleep(time.Second) } } +func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.AgentConfigurations, clientset *kubernetes.Clientset) (s *Scheduler) { + log := logging.NewLogger() + s = NewScheduler(log) + if cfg.OutdatedInterval != "" { + sj, err := NewOutDatedImagesJob(config, js, cfg.OutdatedInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Outdated", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.GetAllInterval != "" { + sj, err := NewKetallJob(config, js, cfg.GetAllInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("GetALL", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.KubeScoreInterval != "" { + sj, err := NewKubescoreJob(clientset, js, cfg.KubeScoreInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("KubeScore", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.RakkessInterval != "" { + sj, err := NewRakkessJob(config, js, cfg.RakkessInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Rakkess", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.KubePreUpgradeInterval != "" { + sj, err := NewKubePreUpgradeJob(config, js, cfg.KubePreUpgradeInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("KubePreUpgrade", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.TrivyInterval != "" { + sj, err := NewTrivyJob(config, js, cfg.TrivyInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Trivy", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + return +} diff --git a/agent/kubviz/scheduler.go b/agent/kubviz/scheduler.go new file mode 100644 index 00000000..ce9b96d3 --- /dev/null +++ b/agent/kubviz/scheduler.go @@ -0,0 +1,88 @@ +package main + +import ( + "sync" + + "github.com/pkg/errors" + "github.com/robfig/cron/v3" + + "github.com/intelops/go-common/logging" +) + +type jobHandler interface { + CronSpec() string + Run() +} + +type Scheduler struct { + log logging.Logger + jobs map[string]jobHandler + cronIDs map[string]cron.EntryID + c *cron.Cron + cronMutex *sync.Mutex +} + +func NewScheduler(log logging.Logger) *Scheduler { + clog := cron.VerbosePrintfLogger(log.(logging.StdLogger)) + return &Scheduler{ + log: log, + c: cron.New(cron.WithChain(cron.SkipIfStillRunning(clog), cron.Recover(clog))), + jobs: map[string]jobHandler{}, + cronIDs: map[string]cron.EntryID{}, + cronMutex: &sync.Mutex{}, + } +} + +func (t *Scheduler) AddJob(jobName string, job jobHandler) error { + t.cronMutex.Lock() + defer t.cronMutex.Unlock() + _, ok := t.cronIDs[jobName] + if ok { + return errors.Errorf("%s job already exists", jobName) + } + spec := job.CronSpec() + if spec == "" { + return errors.Errorf("%s job has no cron spec", jobName) + } + entryID, err := t.c.AddJob(spec, job) + if err != nil { + return errors.WithMessagef(err, "%s job cron spec not valid", jobName) + } + + t.jobs[jobName] = job + t.cronIDs[jobName] = entryID + t.log.Infof("%s job added with cron '%s'", jobName, spec) + return nil +} + +// RemoveJob ... +func (t *Scheduler) RemoveJob(jobName string) error { + t.cronMutex.Lock() + defer t.cronMutex.Unlock() + entryID, ok := t.cronIDs[jobName] + if !ok { + return errors.Errorf("%s job not exist", jobName) + } + + t.c.Remove(entryID) + delete(t.jobs, jobName) + delete(t.cronIDs, jobName) + t.log.Infof("%s job removed", jobName) + return nil +} + +func (t *Scheduler) Start() { + t.c.Start() + t.log.Infof("Job scheduler started") +} + +func (t *Scheduler) Stop() { + t.c.Stop() + t.log.Infof("Job scheduler stopped") +} + +func (t *Scheduler) GetJobs() map[string]jobHandler { + t.cronMutex.Lock() + defer t.cronMutex.Unlock() + return t.jobs +} diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler_watch.go new file mode 100644 index 00000000..90de3d25 --- /dev/null +++ b/agent/kubviz/scheduler_watch.go @@ -0,0 +1,138 @@ +package main + +import ( + "github.com/nats-io/nats.go" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +type OutDatedImagesJob struct { + config *rest.Config + js nats.JetStreamContext + frequency string +} + +type KetallJob struct { + config *rest.Config + js nats.JetStreamContext + frequency string +} +type TrivyJob struct { + config *rest.Config + js nats.JetStreamContext + frequency string +} +type RakkessJob struct { + config *rest.Config + js nats.JetStreamContext + frequency string +} +type KubePreUpgradeJob struct { + config *rest.Config + js nats.JetStreamContext + frequency string +} +type KubescoreJob struct { + clientset *kubernetes.Clientset + js nats.JetStreamContext + frequency string +} + +func NewOutDatedImagesJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*OutDatedImagesJob, error) { + return &OutDatedImagesJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *OutDatedImagesJob) CronSpec() string { + return v.frequency +} + +func (j *OutDatedImagesJob) Run() { + // Call the outDatedImages function with the provided config and js + err := outDatedImages(j.config, j.js) + LogErr(err) +} +func NewKetallJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*KetallJob, error) { + return &KetallJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *KetallJob) CronSpec() string { + return v.frequency +} + +func (j *KetallJob) Run() { + // Call the Ketall function with the provided config and js + err := GetAllResources(j.config, j.js) + LogErr(err) +} + +func NewKubePreUpgradeJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*KubePreUpgradeJob, error) { + return &KubePreUpgradeJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *KubePreUpgradeJob) CronSpec() string { + return v.frequency +} + +func (j *KubePreUpgradeJob) Run() { + // Call the Kubepreupgrade function with the provided config and js + err := GetAllResources(j.config, j.js) + LogErr(err) +} + +func NewKubescoreJob(clientset *kubernetes.Clientset, js nats.JetStreamContext, frequency string) (*KubescoreJob, error) { + return &KubescoreJob{ + clientset: clientset, + js: js, + frequency: frequency, + }, nil +} +func (v *KubescoreJob) CronSpec() string { + return v.frequency +} + +func (j *KubescoreJob) Run() { + // Call the Kubescore function with the provided config and js + err := RunKubeScore(j.clientset, j.js) + LogErr(err) +} +func NewRakkessJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*RakkessJob, error) { + return &RakkessJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *RakkessJob) CronSpec() string { + return v.frequency +} + +func (j *RakkessJob) Run() { + // Call the Rakkes function with the provided config and js + err := RakeesOutput(j.config, j.js) + LogErr(err) +} +func NewTrivyJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivyJob, error) { + return &TrivyJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *TrivyJob) CronSpec() string { + return v.frequency +} + +func (j *TrivyJob) Run() { + // Call the Trivy function with the provided config and js + err := runTrivyScans(j.config, j.js) + LogErr(err) +} diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index adaf84ef..46248c8a 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -1,8 +1,10 @@ package main import ( + "bytes" "encoding/json" "log" + exec "os/exec" "strings" "github.com/aquasecurity/trivy/pkg/k8s/report" @@ -12,11 +14,38 @@ import ( "github.com/nats-io/nats.go" ) +func executeCommandTrivy(command string) ([]byte, error) { + cmd := exec.Command("/bin/sh", "-c", command) + var outc, errc bytes.Buffer + cmd.Stdout = &outc + cmd.Stderr = &errc + + err := cmd.Run() + + if err != nil { + log.Println("Execute Trivy Command Error", err.Error()) + } + + return outc.Bytes(), err +} func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport - out, err := executeCommand("trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") - // log.Println("Commnd for k8s cluster scan: trivy k8s --report summary cluster --timeout 60m -f json -q --cache-dir /tmp/.cache") - parts := strings.SplitN(out, "{", 2) + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 20m -f json --cache-dir /tmp/.cache --debug" + + // Log the command before execution + log.Printf("Executing command: %s\n", cmdString) + + // Execute the command + out, err := executeCommandTrivy(cmdString) + + // Handle errors and process the command output as needed + if err != nil { + log.Printf("Error executing command: %v\n", err) + } + // Log the command output for debugging purposes + log.Printf("Command output: %s\n", out) + outStr := string(out) + parts := strings.SplitN(outStr, "{", 2) if len(parts) <= 1 { log.Println("No output from k8s cluster scan command", err) return err diff --git a/go.mod b/go.mod index 414c54bb..df47ddff 100644 --- a/go.mod +++ b/go.mod @@ -17,12 +17,14 @@ require ( github.com/golang-migrate/migrate/v4 v4.16.2 github.com/google/uuid v1.3.0 github.com/hashicorp/go-version v1.6.0 + github.com/intelops/go-common v1.0.19 github.com/kelseyhightower/envconfig v1.4.0 github.com/nats-io/nats.go v1.27.1 github.com/pkg/errors v0.9.1 + github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 - golang.org/x/term v0.10.0 + golang.org/x/term v0.11.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 k8s.io/cli-runtime v0.27.3 @@ -31,6 +33,9 @@ require ( ) require ( + cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go/iam v1.1.1 // indirect + cloud.google.com/go/storage v1.30.1 // indirect github.com/ClickHouse/ch-go v0.52.1 // indirect github.com/CycloneDX/cyclonedx-go v0.7.2-0.20230625092137-07e2f29defc3 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect @@ -70,6 +75,7 @@ require ( github.com/google/go-containerregistry v0.15.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/googleapis/gax-go/v2 v2.11.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -108,10 +114,10 @@ require ( github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/samber/lo v1.38.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect + github.com/showa-93/go-mask v0.6.0 // indirect github.com/spdx/tools-golang v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect @@ -128,15 +134,18 @@ require ( go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.10.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.11.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.10.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/oauth2 v0.11.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.126.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 1570851a..b8b6d6ff 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,12 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= +cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= +cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -11,6 +14,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/ClickHouse/ch-go v0.52.1 h1:nucdgfD1BDSHjbNaG3VNebonxJzD8fX8jbuBpfo5VY0= github.com/ClickHouse/ch-go v0.52.1/go.mod h1:B9htMJ0hii/zrC2hljUKdnagRBuLqtRG/GrU3jqCwRk= +github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= github.com/ClickHouse/clickhouse-go/v2 v2.10.1 h1:WCnusqEeCO/9sLFVIv57le/O1ydUb+x9+SYYhJ11fsY= github.com/ClickHouse/clickhouse-go/v2 v2.10.1/go.mod h1:teXfZNM90iQ99Jnuht+dxQXCuhDZ8nvvMoTJOFrcmcg= github.com/CycloneDX/cyclonedx-go v0.7.2-0.20230625092137-07e2f29defc3 h1:NqeV+ZMqpcosu0Xg2VW14Ru9ayBs/toe2oihS7sN6Xo= @@ -76,6 +80,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= github.com/containerd/containerd v1.2.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -90,6 +95,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/docker/cli v0.0.0-20190913211141-95327f4e6241/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuDEvFVVDafE= @@ -216,13 +222,14 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= +github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -248,6 +255,8 @@ github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/intelops/go-common v1.0.19 h1:K53TIISpeTONS4g1squwGvo+1wmSbv2Kqp31mpw1090= +github.com/intelops/go-common v1.0.19/go.mod h1:GDDr2xP2uqtjMgATC4BLDt29kC7W9R3EW+8Du2LlNt8= github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -404,6 +413,8 @@ github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/showa-93/go-mask v0.6.0 h1:nNW3dgEocYB7QCGzgRx9wlYrepEg+tRw/keg7u1ftY8= +github.com/showa-93/go-mask v0.6.0/go.mod h1:aswIj007gm0EPAzOGES9ACy1jDm3QT08/LPSClMp410= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -487,8 +498,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -509,11 +520,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -537,18 +548,18 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -568,7 +579,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.121.0 h1:8Oopoo8Vavxx6gt+sgs8s8/X60WBAtKQq6JqnkF+xow= +google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= +google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -577,12 +589,16 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb h1:Isk1sSH7bovx8Rti2wZK0UZF6oraBDK74uoyLEEVFN0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From a5d40cb5ab7a34149f5e8ac08afa15c45f2cfb8a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 11:52:03 +0530 Subject: [PATCH 036/263] change the docker base image in second stage build of client as the previous one does not have a shell --- dockerfiles/migration/Dockerfile | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dockerfiles/migration/Dockerfile b/dockerfiles/migration/Dockerfile index dbd4949e..0281813d 100644 --- a/dockerfiles/migration/Dockerfile +++ b/dockerfiles/migration/Dockerfile @@ -1,22 +1,21 @@ -# Build the manager binary FROM golang:1.20 as builder WORKDIR /workspace -# Copy the Go Modules manifests COPY ./ ./ RUN go mod download -# Build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o migration cmd/cli/main.go +RUN chmod +x /workspace/script/wait-for-clickhouse.sh + # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot +FROM golang:alpine WORKDIR / COPY --from=builder /workspace/migration . -COPY /workspace/sql /sql -COPY /workspace/script /script -RUN chmod +x /script/wait-for-clickhouse.sh +COPY --from=builder /workspace/sql /sql +COPY --from=builder /workspace/script /script + USER 65532:65532 ENTRYPOINT ["/migration"] From e604c5e956c9a8fe2a5e48e755eef97e0140873a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 12:43:40 +0530 Subject: [PATCH 037/263] minor changes in script and dockerfile --- dockerfiles/migration/Dockerfile | 1 + script/wait-for-clickhouse.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dockerfiles/migration/Dockerfile b/dockerfiles/migration/Dockerfile index 0281813d..f2aa1bea 100644 --- a/dockerfiles/migration/Dockerfile +++ b/dockerfiles/migration/Dockerfile @@ -11,6 +11,7 @@ RUN chmod +x /workspace/script/wait-for-clickhouse.sh # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM golang:alpine +RUN apk add --no-cache netcat-openbsd WORKDIR / COPY --from=builder /workspace/migration . COPY --from=builder /workspace/sql /sql diff --git a/script/wait-for-clickhouse.sh b/script/wait-for-clickhouse.sh index 26555ec3..9d7b4692 100644 --- a/script/wait-for-clickhouse.sh +++ b/script/wait-for-clickhouse.sh @@ -7,7 +7,7 @@ MAX_RETRIES=60 retry_count=0 while [ $retry_count -lt $MAX_RETRIES ]; do - if clickhouse-client --host $CLICKHOUSE_HOST --port $CLICKHOUSE_PORT --query "SELECT 1"; then + if nc -z -v -w5 $CLICKHOUSE_HOST $CLICKHOUSE_PORT; then echo "ClickHouse is ready!" exit 0 else From 6424d5839b2e838f762bbf47f09705edfb2c75f7 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 13:01:28 +0530 Subject: [PATCH 038/263] minor bug fix --- cmd/cli/commands/sql.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cli/commands/sql.go b/cmd/cli/commands/sql.go index a615ce1c..07389422 100644 --- a/cmd/cli/commands/sql.go +++ b/cmd/cli/commands/sql.go @@ -7,6 +7,7 @@ import ( "github.com/golang-migrate/migrate/v4" cm "github.com/golang-migrate/migrate/v4/database/clickhouse" + _ "github.com/golang-migrate/migrate/v4/source/file" "github.com/intelops/kubviz/cmd/cli/config" "github.com/spf13/cobra" ) From 8ae014a1a2a6d1a98f380d2fcea657cca4ba747a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 15 Sep 2023 15:59:52 +0530 Subject: [PATCH 039/263] added sql statements for all tables --- sql/20230912051648_clickhouse.down.sql | 20 -- sql/20230912051648_clickhouse.up.sql | 272 ------------------ sql/20230915101223_events.down.sql | 1 + sql/20230915101223_events.up.sql | 15 + sql/20230915101317_rakkess.down.sql | 1 + sql/20230915101317_rakkess.up.sql | 9 + sql/20230915101358_DeprecatedAPIs.down.sql | 1 + sql/20230915101358_DeprecatedAPIs.up.sql | 9 + sql/20230915101437_DeletedAPIs.down.sql | 1 + sql/20230915101437_DeletedAPIs.up.sql | 11 + ...20230915101512_jfrogcontainerpush.down.sql | 1 + sql/20230915101512_jfrogcontainerpush.up.sql | 12 + sql/20230915101549_getall_resources.down.sql | 1 + sql/20230915101549_getall_resources.up.sql | 8 + sql/20230915101643_outdated_images.down.sql | 1 + sql/20230915101643_outdated_images.up.sql | 10 + sql/20230915101736_kubescore.down.sql | 1 + sql/20230915101736_kubescore.up.sql | 7 + sql/20230915101811_trivy_vul.down.sql | 1 + sql/20230915101811_trivy_vul.up.sql | 18 ++ sql/20230915101844_trivy_misconfig.down.sql | 1 + sql/20230915101844_trivy_misconfig.up.sql | 18 ++ sql/20230915101910_trivyimage.down.sql | 1 + sql/20230915101910_trivyimage.up.sql | 14 + sql/20230915102122_dockerhubbuild.down.sql | 1 + sql/20230915102122_dockerhubbuild.up.sql | 9 + ...20230915102157_azurecontainerpush.down.sql | 1 + sql/20230915102157_azurecontainerpush.up.sql | 10 + sql/20230915102229_quaycontainerpush.down.sql | 1 + sql/20230915102229_quaycontainerpush.up.sql | 10 + sql/20230915102314_trivysbom.down.sql | 1 + sql/20230915102314_trivysbom.up.sql | 23 ++ sql/20230915102348_azure_devops.down.sql | 1 + sql/20230915102348_azure_devops.up.sql | 10 + sql/20230915102437_github.down.sql | 1 + sql/20230915102437_github.up.sql | 10 + sql/20230915102739_gitlab.down.sql | 1 + sql/20230915102739_gitlab.up.sql | 10 + sql/20230915102817_bitbucket.down.sql | 1 + sql/20230915102817_bitbucket.up.sql | 10 + sql/20230915102843_gitea.down.sql | 1 + sql/20230915102843_gitea.up.sql | 10 + 42 files changed, 253 insertions(+), 292 deletions(-) delete mode 100644 sql/20230912051648_clickhouse.down.sql delete mode 100644 sql/20230912051648_clickhouse.up.sql create mode 100644 sql/20230915101223_events.down.sql create mode 100644 sql/20230915101223_events.up.sql create mode 100644 sql/20230915101317_rakkess.down.sql create mode 100644 sql/20230915101317_rakkess.up.sql create mode 100644 sql/20230915101358_DeprecatedAPIs.down.sql create mode 100644 sql/20230915101358_DeprecatedAPIs.up.sql create mode 100644 sql/20230915101437_DeletedAPIs.down.sql create mode 100644 sql/20230915101437_DeletedAPIs.up.sql create mode 100644 sql/20230915101512_jfrogcontainerpush.down.sql create mode 100644 sql/20230915101512_jfrogcontainerpush.up.sql create mode 100644 sql/20230915101549_getall_resources.down.sql create mode 100644 sql/20230915101549_getall_resources.up.sql create mode 100644 sql/20230915101643_outdated_images.down.sql create mode 100644 sql/20230915101643_outdated_images.up.sql create mode 100644 sql/20230915101736_kubescore.down.sql create mode 100644 sql/20230915101736_kubescore.up.sql create mode 100644 sql/20230915101811_trivy_vul.down.sql create mode 100644 sql/20230915101811_trivy_vul.up.sql create mode 100644 sql/20230915101844_trivy_misconfig.down.sql create mode 100644 sql/20230915101844_trivy_misconfig.up.sql create mode 100644 sql/20230915101910_trivyimage.down.sql create mode 100644 sql/20230915101910_trivyimage.up.sql create mode 100644 sql/20230915102122_dockerhubbuild.down.sql create mode 100644 sql/20230915102122_dockerhubbuild.up.sql create mode 100644 sql/20230915102157_azurecontainerpush.down.sql create mode 100644 sql/20230915102157_azurecontainerpush.up.sql create mode 100644 sql/20230915102229_quaycontainerpush.down.sql create mode 100644 sql/20230915102229_quaycontainerpush.up.sql create mode 100644 sql/20230915102314_trivysbom.down.sql create mode 100644 sql/20230915102314_trivysbom.up.sql create mode 100644 sql/20230915102348_azure_devops.down.sql create mode 100644 sql/20230915102348_azure_devops.up.sql create mode 100644 sql/20230915102437_github.down.sql create mode 100644 sql/20230915102437_github.up.sql create mode 100644 sql/20230915102739_gitlab.down.sql create mode 100644 sql/20230915102739_gitlab.up.sql create mode 100644 sql/20230915102817_bitbucket.down.sql create mode 100644 sql/20230915102817_bitbucket.up.sql create mode 100644 sql/20230915102843_gitea.down.sql create mode 100644 sql/20230915102843_gitea.up.sql diff --git a/sql/20230912051648_clickhouse.down.sql b/sql/20230912051648_clickhouse.down.sql deleted file mode 100644 index 5cbad299..00000000 --- a/sql/20230912051648_clickhouse.down.sql +++ /dev/null @@ -1,20 +0,0 @@ -DROP TABLE IF EXISTS events; -DROP TABLE IF EXISTS rakkess; -DROP TABLE IF EXISTS DeprecatedAPIs; -DROP TABLE IF EXISTS DeletedAPIs; -DROP TABLE IF EXISTS jfrogcontainerpush; -DROP TABLE IF EXISTS getall_resources; -DROP TABLE IF EXISTS outdated_images; -DROP TABLE IF EXISTS kubescore; -DROP TABLE IF EXISTS trivy_vul; -DROP TABLE IF EXISTS trivy_misconfig; -DROP TABLE IF EXISTS trivyimage; -DROP TABLE IF EXISTS dockerhubbuild; -DROP TABLE IF EXISTS azurecontainerpush; -DROP TABLE IF EXISTS quaycontainerpush; -DROP TABLE IF EXISTS trivysbom; -DROP TABLE IF EXISTS azure_devops; -DROP TABLE IF EXISTS github; -DROP TABLE IF EXISTS gitlab; -DROP TABLE IF EXISTS bitbucket; -DROP TABLE IF EXISTS gitea; diff --git a/sql/20230912051648_clickhouse.up.sql b/sql/20230912051648_clickhouse.up.sql deleted file mode 100644 index a53ee9a9..00000000 --- a/sql/20230912051648_clickhouse.up.sql +++ /dev/null @@ -1,272 +0,0 @@ --- kubvizTable -CREATE TABLE IF NOT EXISTS events ( - ClusterName String, - Id String, - EventTime DateTime('UTC'), - OpType String, - Name String, - Namespace String, - Kind String, - Message String, - Reason String, - Host String, - Event String, - FirstTime String, - LastTime String -) engine=File(TabSeparated); - --- rakeesTable -CREATE TABLE IF NOT EXISTS rakkess ( - ClusterName String, - Name String, - Create String, - Delete String, - List String, - Update String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- kubePugDepricatedTable -CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( - ClusterName String, - ObjectName String, - Description String, - Kind String, - Deprecated UInt8, - Scope String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- kubepugDeletedTable -CREATE TABLE IF NOT EXISTS DeletedAPIs ( - ClusterName String, - ObjectName String, - Group String, - Kind String, - Version String, - Name String, - Deleted UInt8, - Scope String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- jfrogContainerPushEventTable -CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( - Domain String, - EventType String, - RegistryURL String, - RepositoryName String, - SHAID String, - Size Int32, - ImageName String, - Tag String, - Event String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- ketallTable -CREATE TABLE IF NOT EXISTS getall_resources ( - ClusterName String, - Namespace String, - Kind String, - Resource String, - Age String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- outdateTable -CREATE TABLE IF NOT EXISTS outdated_images ( - ClusterName String, - Namespace String, - Pod String, - CurrentImage String, - CurrentTag String, - LatestVersion String, - VersionsBehind Int64, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- kubescoreTable -CREATE TABLE IF NOT EXISTS kubescore ( - id UUID, - namespace String, - cluster_name String, - recommendations String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- trivyTableVul -CREATE TABLE IF NOT EXISTS trivy_vul ( - id UUID, - cluster_name String, - namespace String, - kind String, - name String, - vul_id String, - vul_vendor_ids String, - vul_pkg_id String, - vul_pkg_name String, - vul_pkg_path String, - vul_installed_version String, - vul_fixed_version String, - vul_title String, - vul_severity String, - vul_published_date DateTime('UTC'), - vul_last_modified_date DateTime('UTC') -) engine=File(TabSeparated); - --- trivyTableMisconfig -CREATE TABLE IF NOT EXISTS trivy_misconfig ( - id UUID, - cluster_name String, - namespace String, - kind String, - name String, - misconfig_id String, - misconfig_avdid String, - misconfig_type String, - misconfig_title String, - misconfig_desc String, - misconfig_msg String, - misconfig_query String, - misconfig_resolution String, - misconfig_severity String, - misconfig_status String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- trivyTableImage -CREATE TABLE IF NOT EXISTS trivyimage ( - id UUID, - cluster_name String, - artifact_name String, - vul_id String, - vul_pkg_id String, - vul_pkg_name String, - vul_installed_version String, - vul_fixed_version String, - vul_title String, - vul_severity String, - vul_published_date DateTime('UTC'), - vul_last_modified_date DateTime('UTC') -) engine=File(TabSeparated); - --- dockerHubBuildTable -CREATE TABLE IF NOT EXISTS dockerhubbuild ( - PushedBy String, - ImageTag String, - RepositoryName String, - DateCreated String, - Owner String, - Event String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- azureContainerPushEventTable -CREATE TABLE IF NOT EXISTS azurecontainerpush ( - RegistryURL String, - RepositoryName String, - Tag String, - ImageName String, - Event String, - Size Int32, - SHAID String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- quayContainerPushEventTable -CREATE TABLE IF NOT EXISTS quaycontainerpush ( - name String, - repository String, - nameSpace String, - dockerURL String, - homePage String, - tag String, - Event String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); - --- trivySbomTable -CREATE TABLE IF NOT EXISTS trivysbom ( - id UUID, - schema String, - bom_format String, - spec_version String, - serial_number String, - version INTEGER, - metadata_timestamp DateTime('UTC'), - metatool_vendor String, - metatool_name String, - metatool_version String, - component_bom_ref String, - component_type String, - component_name String, - component_version String, - component_property_name String, - component_property_value String, - component_hash_alg String, - component_hash_content String, - component_license_exp String, - component_purl String, - dependency_ref String -) engine=File(TabSeparated); - --- AzureDevopsTable -CREATE TABLE IF NOT EXISTS azure_devops ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); - --- GithubTable -CREATE TABLE IF NOT EXISTS github ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); - --- GitlabTable -CREATE TABLE IF NOT EXISTS gitlab ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); - --- BitbucketTable -CREATE TABLE IF NOT EXISTS bitbucket ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); - --- GiteaTable -CREATE TABLE IF NOT EXISTS gitea ( - Author String, - Provider String, - CommitID String, - CommitUrl String, - EventType String, - RepoName String, - TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); diff --git a/sql/20230915101223_events.down.sql b/sql/20230915101223_events.down.sql new file mode 100644 index 00000000..653c7400 --- /dev/null +++ b/sql/20230915101223_events.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS events; diff --git a/sql/20230915101223_events.up.sql b/sql/20230915101223_events.up.sql new file mode 100644 index 00000000..b857a489 --- /dev/null +++ b/sql/20230915101223_events.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS events ( + ClusterName String, + Id String, + EventTime DateTime('UTC'), + OpType String, + Name String, + Namespace String, + Kind String, + Message String, + Reason String, + Host String, + Event String, + FirstTime String, + LastTime String +) engine=File(TabSeparated); diff --git a/sql/20230915101317_rakkess.down.sql b/sql/20230915101317_rakkess.down.sql new file mode 100644 index 00000000..e182c3cb --- /dev/null +++ b/sql/20230915101317_rakkess.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS rakkess; diff --git a/sql/20230915101317_rakkess.up.sql b/sql/20230915101317_rakkess.up.sql new file mode 100644 index 00000000..e297366c --- /dev/null +++ b/sql/20230915101317_rakkess.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS rakkess ( + ClusterName String, + Name String, + Create String, + Delete String, + List String, + Update String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101358_DeprecatedAPIs.down.sql b/sql/20230915101358_DeprecatedAPIs.down.sql new file mode 100644 index 00000000..af77c59a --- /dev/null +++ b/sql/20230915101358_DeprecatedAPIs.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS DeprecatedAPIs; diff --git a/sql/20230915101358_DeprecatedAPIs.up.sql b/sql/20230915101358_DeprecatedAPIs.up.sql new file mode 100644 index 00000000..27dc8248 --- /dev/null +++ b/sql/20230915101358_DeprecatedAPIs.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( + ClusterName String, + ObjectName String, + Description String, + Kind String, + Deprecated UInt8, + Scope String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101437_DeletedAPIs.down.sql b/sql/20230915101437_DeletedAPIs.down.sql new file mode 100644 index 00000000..e0157f5e --- /dev/null +++ b/sql/20230915101437_DeletedAPIs.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS DeletedAPIs; diff --git a/sql/20230915101437_DeletedAPIs.up.sql b/sql/20230915101437_DeletedAPIs.up.sql new file mode 100644 index 00000000..4a61f7a8 --- /dev/null +++ b/sql/20230915101437_DeletedAPIs.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS DeletedAPIs ( + ClusterName String, + ObjectName String, + Group String, + Kind String, + Version String, + Name String, + Deleted UInt8, + Scope String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101512_jfrogcontainerpush.down.sql b/sql/20230915101512_jfrogcontainerpush.down.sql new file mode 100644 index 00000000..03c41863 --- /dev/null +++ b/sql/20230915101512_jfrogcontainerpush.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS jfrogcontainerpush; diff --git a/sql/20230915101512_jfrogcontainerpush.up.sql b/sql/20230915101512_jfrogcontainerpush.up.sql new file mode 100644 index 00000000..60c6f058 --- /dev/null +++ b/sql/20230915101512_jfrogcontainerpush.up.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( + Domain String, + EventType String, + RegistryURL String, + RepositoryName String, + SHAID String, + Size Int32, + ImageName String, + Tag String, + Event String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101549_getall_resources.down.sql b/sql/20230915101549_getall_resources.down.sql new file mode 100644 index 00000000..866aaa72 --- /dev/null +++ b/sql/20230915101549_getall_resources.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS getall_resources; diff --git a/sql/20230915101549_getall_resources.up.sql b/sql/20230915101549_getall_resources.up.sql new file mode 100644 index 00000000..2263f53c --- /dev/null +++ b/sql/20230915101549_getall_resources.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS getall_resources ( + ClusterName String, + Namespace String, + Kind String, + Resource String, + Age String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101643_outdated_images.down.sql b/sql/20230915101643_outdated_images.down.sql new file mode 100644 index 00000000..4741b3c3 --- /dev/null +++ b/sql/20230915101643_outdated_images.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS outdated_images; diff --git a/sql/20230915101643_outdated_images.up.sql b/sql/20230915101643_outdated_images.up.sql new file mode 100644 index 00000000..44e67bcf --- /dev/null +++ b/sql/20230915101643_outdated_images.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS outdated_images ( + ClusterName String, + Namespace String, + Pod String, + CurrentImage String, + CurrentTag String, + LatestVersion String, + VersionsBehind Int64, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101736_kubescore.down.sql b/sql/20230915101736_kubescore.down.sql new file mode 100644 index 00000000..db35ad56 --- /dev/null +++ b/sql/20230915101736_kubescore.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS kubescore; diff --git a/sql/20230915101736_kubescore.up.sql b/sql/20230915101736_kubescore.up.sql new file mode 100644 index 00000000..0f18bfb7 --- /dev/null +++ b/sql/20230915101736_kubescore.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS kubescore ( + id UUID, + namespace String, + cluster_name String, + recommendations String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101811_trivy_vul.down.sql b/sql/20230915101811_trivy_vul.down.sql new file mode 100644 index 00000000..52940cbf --- /dev/null +++ b/sql/20230915101811_trivy_vul.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trivy_vul; diff --git a/sql/20230915101811_trivy_vul.up.sql b/sql/20230915101811_trivy_vul.up.sql new file mode 100644 index 00000000..f141ff03 --- /dev/null +++ b/sql/20230915101811_trivy_vul.up.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS trivy_vul ( + id UUID, + cluster_name String, + namespace String, + kind String, + name String, + vul_id String, + vul_vendor_ids String, + vul_pkg_id String, + vul_pkg_name String, + vul_pkg_path String, + vul_installed_version String, + vul_fixed_version String, + vul_title String, + vul_severity String, + vul_published_date DateTime('UTC'), + vul_last_modified_date DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101844_trivy_misconfig.down.sql b/sql/20230915101844_trivy_misconfig.down.sql new file mode 100644 index 00000000..518dab21 --- /dev/null +++ b/sql/20230915101844_trivy_misconfig.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trivy_misconfig; diff --git a/sql/20230915101844_trivy_misconfig.up.sql b/sql/20230915101844_trivy_misconfig.up.sql new file mode 100644 index 00000000..3971b40e --- /dev/null +++ b/sql/20230915101844_trivy_misconfig.up.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS trivy_misconfig ( + id UUID, + cluster_name String, + namespace String, + kind String, + name String, + misconfig_id String, + misconfig_avdid String, + misconfig_type String, + misconfig_title String, + misconfig_desc String, + misconfig_msg String, + misconfig_query String, + misconfig_resolution String, + misconfig_severity String, + misconfig_status String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915101910_trivyimage.down.sql b/sql/20230915101910_trivyimage.down.sql new file mode 100644 index 00000000..54ee8f76 --- /dev/null +++ b/sql/20230915101910_trivyimage.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trivyimage; diff --git a/sql/20230915101910_trivyimage.up.sql b/sql/20230915101910_trivyimage.up.sql new file mode 100644 index 00000000..f6781862 --- /dev/null +++ b/sql/20230915101910_trivyimage.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS trivyimage ( + id UUID, + cluster_name String, + artifact_name String, + vul_id String, + vul_pkg_id String, + vul_pkg_name String, + vul_installed_version String, + vul_fixed_version String, + vul_title String, + vul_severity String, + vul_published_date DateTime('UTC'), + vul_last_modified_date DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915102122_dockerhubbuild.down.sql b/sql/20230915102122_dockerhubbuild.down.sql new file mode 100644 index 00000000..2cc2ee1d --- /dev/null +++ b/sql/20230915102122_dockerhubbuild.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS dockerhubbuild; diff --git a/sql/20230915102122_dockerhubbuild.up.sql b/sql/20230915102122_dockerhubbuild.up.sql new file mode 100644 index 00000000..5d770afd --- /dev/null +++ b/sql/20230915102122_dockerhubbuild.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS dockerhubbuild ( + PushedBy String, + ImageTag String, + RepositoryName String, + DateCreated String, + Owner String, + Event String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915102157_azurecontainerpush.down.sql b/sql/20230915102157_azurecontainerpush.down.sql new file mode 100644 index 00000000..3d043e15 --- /dev/null +++ b/sql/20230915102157_azurecontainerpush.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS azurecontainerpush; diff --git a/sql/20230915102157_azurecontainerpush.up.sql b/sql/20230915102157_azurecontainerpush.up.sql new file mode 100644 index 00000000..f603a09c --- /dev/null +++ b/sql/20230915102157_azurecontainerpush.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS azurecontainerpush ( + RegistryURL String, + RepositoryName String, + Tag String, + ImageName String, + Event String, + Size Int32, + SHAID String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915102229_quaycontainerpush.down.sql b/sql/20230915102229_quaycontainerpush.down.sql new file mode 100644 index 00000000..e157e46a --- /dev/null +++ b/sql/20230915102229_quaycontainerpush.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS quaycontainerpush; diff --git a/sql/20230915102229_quaycontainerpush.up.sql b/sql/20230915102229_quaycontainerpush.up.sql new file mode 100644 index 00000000..c79d0108 --- /dev/null +++ b/sql/20230915102229_quaycontainerpush.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS quaycontainerpush ( + name String, + repository String, + nameSpace String, + dockerURL String, + homePage String, + tag String, + Event String, + EventTime DateTime('UTC') +) engine=File(TabSeparated); diff --git a/sql/20230915102314_trivysbom.down.sql b/sql/20230915102314_trivysbom.down.sql new file mode 100644 index 00000000..af2515cb --- /dev/null +++ b/sql/20230915102314_trivysbom.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trivysbom; diff --git a/sql/20230915102314_trivysbom.up.sql b/sql/20230915102314_trivysbom.up.sql new file mode 100644 index 00000000..4efa46a3 --- /dev/null +++ b/sql/20230915102314_trivysbom.up.sql @@ -0,0 +1,23 @@ +CREATE TABLE IF NOT EXISTS trivysbom ( + id UUID, + schema String, + bom_format String, + spec_version String, + serial_number String, + version INTEGER, + metadata_timestamp DateTime('UTC'), + metatool_vendor String, + metatool_name String, + metatool_version String, + component_bom_ref String, + component_type String, + component_name String, + component_version String, + component_property_name String, + component_property_value String, + component_hash_alg String, + component_hash_content String, + component_license_exp String, + component_purl String, + dependency_ref String +) engine=File(TabSeparated); diff --git a/sql/20230915102348_azure_devops.down.sql b/sql/20230915102348_azure_devops.down.sql new file mode 100644 index 00000000..e6227a49 --- /dev/null +++ b/sql/20230915102348_azure_devops.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS azure_devops; diff --git a/sql/20230915102348_azure_devops.up.sql b/sql/20230915102348_azure_devops.up.sql new file mode 100644 index 00000000..00f561b8 --- /dev/null +++ b/sql/20230915102348_azure_devops.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS azure_devops ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); diff --git a/sql/20230915102437_github.down.sql b/sql/20230915102437_github.down.sql new file mode 100644 index 00000000..dd91d720 --- /dev/null +++ b/sql/20230915102437_github.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS github; diff --git a/sql/20230915102437_github.up.sql b/sql/20230915102437_github.up.sql new file mode 100644 index 00000000..3adb6d79 --- /dev/null +++ b/sql/20230915102437_github.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS github ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); diff --git a/sql/20230915102739_gitlab.down.sql b/sql/20230915102739_gitlab.down.sql new file mode 100644 index 00000000..769364c9 --- /dev/null +++ b/sql/20230915102739_gitlab.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS gitlab; diff --git a/sql/20230915102739_gitlab.up.sql b/sql/20230915102739_gitlab.up.sql new file mode 100644 index 00000000..737c66d4 --- /dev/null +++ b/sql/20230915102739_gitlab.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS gitlab ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); diff --git a/sql/20230915102817_bitbucket.down.sql b/sql/20230915102817_bitbucket.down.sql new file mode 100644 index 00000000..b0df312d --- /dev/null +++ b/sql/20230915102817_bitbucket.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS bitbucket; diff --git a/sql/20230915102817_bitbucket.up.sql b/sql/20230915102817_bitbucket.up.sql new file mode 100644 index 00000000..d8f70809 --- /dev/null +++ b/sql/20230915102817_bitbucket.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS bitbucket ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); diff --git a/sql/20230915102843_gitea.down.sql b/sql/20230915102843_gitea.down.sql new file mode 100644 index 00000000..9c24ca85 --- /dev/null +++ b/sql/20230915102843_gitea.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS gitea; diff --git a/sql/20230915102843_gitea.up.sql b/sql/20230915102843_gitea.up.sql new file mode 100644 index 00000000..e8ef86fd --- /dev/null +++ b/sql/20230915102843_gitea.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS gitea ( + Author String, + Provider String, + CommitID String, + CommitUrl String, + EventType String, + RepoName String, + TimeStamp DateTime('UTC'), + Event String +) engine=File(TabSeparated); From 86f77f0ef11b357ba219cd5b2b3154c5272df248 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Sat, 16 Sep 2023 08:00:10 +0530 Subject: [PATCH 040/263] Added seperate dashboards for Git providers --- grafana/azure-dashboard.json | 479 +++++++ grafana/bitBucket-dashboard.json | 479 +++++++ grafana/giTea-dashboard.json | 479 +++++++ grafana/gitBridge-dashboard.json | 2079 ------------------------------ grafana/gitHub-dashboard.json | 456 +++++++ grafana/gitLab-dashboard.json | 491 +++++++ grafana/kubeData-dashboard.json | 74 +- grafana/kubvizDsahboard.json | 589 +++++---- 8 files changed, 2772 insertions(+), 2354 deletions(-) create mode 100644 grafana/azure-dashboard.json create mode 100644 grafana/bitBucket-dashboard.json create mode 100644 grafana/giTea-dashboard.json delete mode 100644 grafana/gitBridge-dashboard.json create mode 100644 grafana/gitHub-dashboard.json create mode 100644 grafana/gitLab-dashboard.json diff --git a/grafana/azure-dashboard.json b/grafana/azure-dashboard.json new file mode 100644 index 00000000..ed13de74 --- /dev/null +++ b/grafana/azure-dashboard.json @@ -0,0 +1,479 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 22, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"azure_devops\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"azure_devops\" \nWHERE TimeStamp >= toDateTime(1694534229) AND TimeStamp <= toDateTime(1694620629) AND EventType IN ('git.push') AND Author IN ('Anila Soman')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Contributions", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534188) AND TimeStamp <= toDateTime(1694620588) AND EventType = 'git.push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Azure push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.azure_devops \nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.azure_devops \nWHERE TimeStamp >= toDateTime(1694534208) AND TimeStamp <= toDateTime(1694620608) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Azure Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-red", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534146) AND TimeStamp <= toDateTime(1694620546) AND EventType = 'git.push'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Push event counts", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-red", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534169) AND TimeStamp <= toDateTime(1694620569) AND EventType = 'git.pullrequest.merged'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Merge events count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)", + "rawQuery": "SELECT * FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534128) AND TimeStamp <= toDateTime(1694620528) AND EventType IN ('git.push') AND Author IN ('Anila Soman')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT EventType FROM default.azure_devops", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.azure_devops", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT Author FROM default.azure_devops", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.azure_devops", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Azure", + "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", + "version": 1, + "weekStart": "" +} + diff --git a/grafana/bitBucket-dashboard.json b/grafana/bitBucket-dashboard.json new file mode 100644 index 00000000..43701d83 --- /dev/null +++ b/grafana/bitBucket-dashboard.json @@ -0,0 +1,479 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 24, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"bitbucket\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType In ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"bitbucket\" \nWHERE TimeStamp >= toDateTime(1694536440) AND TimeStamp <= toDateTime(1694622840) AND EventType In ('repo:push','pullrequest:created','pullrequest:fulfilled') AND Author IN ('')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Events", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536383) AND TimeStamp <= toDateTime(1694622783) AND EventType = 'repo:push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of BitBucket Push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536421) AND TimeStamp <= toDateTime(1694622821) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of BitBucket Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536343) AND TimeStamp <= toDateTime(1694622743) AND EventType = 'repo:push'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Push Events Count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536364) AND TimeStamp <= toDateTime(1694622764) AND EventType = 'pullrequest:fulfilled'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Merge events count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author In ($Author)", + "rawQuery": "SELECT * FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536323) AND TimeStamp <= toDateTime(1694622723) AND EventType IN ('repo:push','pullrequest:created','pullrequest:fulfilled') AND Author In ('')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT EventType FROM default.bitbucket", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.bitbucket", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT Author FROM default.bitbucket", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.bitbucket", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BitBucket", + "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", + "version": 2, + "weekStart": "" +} + diff --git a/grafana/giTea-dashboard.json b/grafana/giTea-dashboard.json new file mode 100644 index 00000000..0b103eb3 --- /dev/null +++ b/grafana/giTea-dashboard.json @@ -0,0 +1,479 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 23, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitea\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitea\" \nWHERE TimeStamp >= toDateTime(1694535218) AND TimeStamp <= toDateTime(1694621618) AND EventType IN ('push','pull_request') AND Author IN ('')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GiTea Contributions", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535157) AND TimeStamp <= toDateTime(1694621557) AND EventType = 'push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Gitea push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535179) AND TimeStamp <= toDateTime(1694621579) AND EventType = 'pull_request'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Gitea Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", + "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535111) AND TimeStamp <= toDateTime(1694621511) AND EventType = 'push'", + "refId": "A", + "round": "0s", + "skip_comments": false + } + ], + "title": "BitBucket Push event count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535137) AND TimeStamp <= toDateTime(1694621537) AND EventType = 'pull_request'", + "refId": "A", + "round": "0s", + "skip_comments": false + } + ], + "title": "BitBucket Merge events count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)", + "rawQuery": "SELECT * FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535089) AND TimeStamp <= toDateTime(1694621489) AND EventType IN ('push','pull_request') AND Author IN ('')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GiTea Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT EventType FROM default.gitea", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.gitea", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT Author FROM default.gitea", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.gitea", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "GiTea", + "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", + "version": 2, + "weekStart": "" +} + diff --git a/grafana/gitBridge-dashboard.json b/grafana/gitBridge-dashboard.json deleted file mode 100644 index 2f9b61c6..00000000 --- a/grafana/gitBridge-dashboard.json +++ /dev/null @@ -1,2079 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 9, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 0, - "y": 0 - }, - "id": 42, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694224447) AND TimeStamp <= toDateTime(1694246047) AND EventType = 'push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITHUB Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "yellow", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 5, - "y": 0 - }, - "id": 43, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'", - "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694224469) AND TimeStamp <= toDateTime(1694246069) AND EventType = 'Push Hook'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITLAB Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-blue", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 9, - "y": 0 - }, - "id": 44, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224493) AND TimeStamp <= toDateTime(1694246093) AND EventType = 'repo:push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BITBUCKET Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "orange", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 14, - "y": 0 - }, - "id": 45, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", - "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224513) AND TimeStamp <= toDateTime(1694246113) AND EventType = 'push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITEA Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-red", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 19, - "y": 0 - }, - "id": 46, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224533) AND TimeStamp <= toDateTime(1694246133) AND EventType = 'git.push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "AZURE Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 0, - "y": 5 - }, - "id": 37, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694224285) AND TimeStamp <= toDateTime(1694245885) AND EventType = 'pull_request'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITHUB Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "yellow", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 5, - "y": 5 - }, - "id": 38, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'", - "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694224307) AND TimeStamp <= toDateTime(1694245907) AND EventType = 'Merge Request Hook'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITLAB Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-blue", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 9, - "y": 5 - }, - "id": 39, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224327) AND TimeStamp <= toDateTime(1694245927) AND EventType = 'pullrequest:fulfilled'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BITBUCKET Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "orange", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 14, - "y": 5 - }, - "id": 40, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224386) AND TimeStamp <= toDateTime(1694245986) AND EventType = 'pull_request'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITEA Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-red", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 19, - "y": 5 - }, - "id": 41, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224413) AND TimeStamp <= toDateTime(1694246013) AND EventType = 'git.pullrequest.merged'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "AZURE Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of Push events on Azure by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#17bcc1", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 36, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Azure_Push_Events\nFROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Azure_Push_Events\nFROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224224) AND TimeStamp <= toDateTime(1694245824) AND EventType = 'git.push'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Azure Push Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of Merge events on Azure by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#17bcc1", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 10 - }, - "id": 34, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Azure_Merge_Events\nFROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Azure_Merge_Events\nFROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694224264) AND TimeStamp <= toDateTime(1694245864) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Azure Merge Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of Push events on BitBucket by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#e8f808", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 18 - }, - "id": 28, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224141) AND TimeStamp <= toDateTime(1694245741) AND EventType = 'repo:push'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BitBucket Push Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of Merge events on BitBucket by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#e8f808", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 18 - }, - "id": 30, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694224185) AND TimeStamp <= toDateTime(1694245785) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BitBucket Merge Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of Push events on GiTea by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#f46565", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 26 - }, - "id": 24, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224046) AND TimeStamp <= toDateTime(1694245646) AND EventType = 'push'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GiTea Push Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of Merge events on GiTea by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#f46565", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 26 - }, - "id": 26, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694224094) AND TimeStamp <= toDateTime(1694245694) AND EventType = 'pull_request'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GiTea Merge Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of push events on GitLab by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 34 - }, - "id": 20, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694223949) AND TimeStamp <= toDateTime(1694245549) AND EventType = 'Push Hook'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GitLab Push Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of merge events on GitLab by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 34 - }, - "id": 22, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694224003) AND TimeStamp <= toDateTime(1694245603) AND EventType = 'Merge Request Hook'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GitLab Merge Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of push events on Github by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "left", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 44 - }, - "id": 16, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694223871) AND TimeStamp <= toDateTime(1694245471) AND EventType = 'push'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Github Push Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "The panel displays the count of merge events on Github by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 44 - }, - "id": 18, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", - "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694223910) AND TimeStamp <= toDateTime(1694245510) AND EventType = 'pull_request'\nGROUP BY Author", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Github Merge Events by Author", - "type": "barchart" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays all the events occured in the Azure Procider", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 54 - }, - "id": 14, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", - "rawQuery": "SELECT * FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694223793) AND TimeStamp <= toDateTime(1694245393)\nORDER BY TimeStamp DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Azure", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays all the events occured in the BitBucket Providers", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 61 - }, - "id": 12, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", - "rawQuery": "SELECT * FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694223708) AND TimeStamp <= toDateTime(1694245308)\nORDER BY TimeStamp DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BitBucket", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays all the events occured in the GiTea provider", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 69 - }, - "id": 10, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", - "rawQuery": "SELECT * FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694223444) AND TimeStamp <= toDateTime(1694245044)\nORDER BY TimeStamp DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GiTea", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays all the events occured in the GitLab provider.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 78 - }, - "id": 8, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", - "rawQuery": "SELECT * FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694223029) AND TimeStamp <= toDateTime(1694244629)\nORDER BY TimeStamp DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GitLab", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays all the events occured in the GitHub provider.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 86 - }, - "id": 6, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.github\nWHERE $timeFilterByColumn(TimeStamp)\nORDER BY TimeStamp DESC", - "rawQuery": "SELECT * FROM default.github\nWHERE TimeStamp >= toDateTime(1694222878) AND TimeStamp <= toDateTime(1694244478)\nORDER BY TimeStamp DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GitHub", - "type": "table" - } - ], - "refresh": "", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-24h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "GitBridge", - "uid": "u3EJcUqVk", - "version": 2, - "weekStart": "" -} \ No newline at end of file diff --git a/grafana/gitHub-dashboard.json b/grafana/gitHub-dashboard.json new file mode 100644 index 00000000..59a7cda2 --- /dev/null +++ b/grafana/gitHub-dashboard.json @@ -0,0 +1,456 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 20, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"github\" \nWHERE $timeFilterByColumn(TimeStamp) AND Author IN ($Author) AND EventType IN ($eventType)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"github\" \nWHERE TimeStamp >= toDateTime(1694597007) AND TimeStamp <= toDateTime(1694618607) AND Author IN ('ahinvinith') AND EventType IN ('push','pull_request')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitHub Contributors", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n right: '5%', // Adjust the right margin to position it on the top right\n top: '5%', // Adjust the top margin to position it on the top right\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694596958) AND TimeStamp <= toDateTime(1694618558) AND EventType = 'push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitHub push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694596980) AND TimeStamp <= toDateTime(1694618580) AND EventType = 'pull_request'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitHub Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694596901) AND TimeStamp <= toDateTime(1694618501) AND EventType = 'pull_request'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitHub Merge event Count", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694596932) AND TimeStamp <= toDateTime(1694618532) AND EventType = 'push'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitHub Push events count", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.github\nWHERE TimeStamp >= toDateTime(1694596869) AND TimeStamp <= toDateTime(1694618469) AND EventType IN ('push','pull_request') AND Author IN ('ahinvinith')\nORDER BY TimeStamp DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Github Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "select Author from default.github", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "select Author from default.github", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "select EventType from default.github", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "select EventType from default.github", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "GitHub", + "uid": "ef91218c-94cb-48b1-be1d-0bafe848b75c", + "version": 1, + "weekStart": "" +} + diff --git a/grafana/gitLab-dashboard.json b/grafana/gitLab-dashboard.json new file mode 100644 index 00000000..986eb838 --- /dev/null +++ b/grafana/gitLab-dashboard.json @@ -0,0 +1,491 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 21, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitlab\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType In ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitlab\" \nWHERE TimeStamp >= toDateTime(1694533326) AND TimeStamp <= toDateTime(1694619726) AND EventType In ('Push Hook') AND Author IN ('Ahin Vinith')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Contributions", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533277) AND TimeStamp <= toDateTime(1694619677) AND EventType = 'Push Hook'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitLab Push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533298) AND TimeStamp <= toDateTime(1694619698) AND EventType = 'Merge Request Hook'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitHub Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'", + "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533230) AND TimeStamp <= toDateTime(1694619630) AND EventType = 'Push Hook'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Push Events", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'", + "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533256) AND TimeStamp <= toDateTime(1694619656) AND EventType = 'Merge Request Hook'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Merge events", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)", + "rawQuery": "SELECT * FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533200) AND TimeStamp <= toDateTime(1694619600) AND EventType IN ('Push Hook') AND Author IN ('Ahin Vinith')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT EventType FROM default.gitlab", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.gitlab", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "definition": "SELECT Author FROM default.gitlab", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.gitlab", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "GitLab", + "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", + "version": 2, + "weekStart": "" +} + diff --git a/grafana/kubeData-dashboard.json b/grafana/kubeData-dashboard.json index 7768bb2e..cf63956d 100644 --- a/grafana/kubeData-dashboard.json +++ b/grafana/kubeData-dashboard.json @@ -21,10 +21,69 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, + "id": 19, "links": [], "liveNow": false, "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract data from your JSON\nconst clusterNames = data.series[0].fields[0].values; // New column for ClusterName\nconst namespaces = data.series[0].fields[1].values;\nconst kinds = data.series[0].fields[2].values; // Adjusted index for Kind\nconst counts = data.series[0].fields[3].values; // New column for Count\n\n// Create a hierarchical structure from the data without a root node\nconst hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n};\n\nconst seenClusterNames = new Set();\nconst seenNamespaces = new Set();\n\nfor (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n}\n\n// Create the tree chart using ECharts\nconst option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 14, // Increase the text size for regular nodes\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 14, // Increase the text size for leaves\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n};\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Namespace, Kind, count(*) AS count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Namespace IN ($namespace)\nGROUP BY ClusterName, Namespace, Kind", + "rawQuery": "SELECT ClusterName, Namespace, Kind, count(*) AS count\nFROM default.events\nWHERE EventTime >= toDateTime(1694603943) AND EventTime <= toDateTime(1694604243) AND ClusterName IN ('dev') AND Namespace IN ('kubviz','argocd','observability','default','tracetestdemo','sonarqube','kube-system','tek','quality','tekton-pipelines','sample','tekton-pipelines-resolvers','tekton-chains','cert-manager','qtapp','otel-collector','mysql','traefik')\nGROUP BY ClusterName, Namespace, Kind", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Kubernetes Workload", + "type": "volkovlabs-echarts-panel" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -58,7 +117,7 @@ "h": 7, "w": 24, "x": 0, - "y": 0 + "y": 11 }, "id": 4, "options": { @@ -137,7 +196,7 @@ "h": 16, "w": 24, "x": 0, - "y": 7 + "y": 18 }, "id": 2, "options": { @@ -238,6 +297,11 @@ "uid": "vertamedia-clickhouse-datasource" }, "definition": "SELECT Kind FROM default.events", + "error": { + "data": { + "message": "Unexpected error" + } + }, "hide": 0, "includeAll": true, "multi": true, @@ -283,6 +347,6 @@ "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 1, + "version": 3, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index cc4b8a44..1383336b 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 8, + "id": 5, "links": [], "liveNow": false, "panels": [ @@ -114,7 +114,13 @@ "color": { "mode": "thresholds" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -184,7 +190,13 @@ "color": { "mode": "thresholds" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Outdated Images", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -233,8 +245,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", - "rawQuery": "SELECT count(*) FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694243264) AND EventTime <= toDateTime(1694243564) AND VersionsBehind > 0", + "query": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", + "rawQuery": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true @@ -254,7 +266,13 @@ "color": { "mode": "thresholds" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Kubedata", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -324,7 +342,13 @@ "color": { "mode": "thresholds" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "DeletedAPIs", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -373,8 +397,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT count(*) FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694243293) AND EventTime <= toDateTime(1694243593)", + "query": "SELECT count(*) FROM default.DeletedAPIs", + "rawQuery": "SELECT count(*) FROM default.DeletedAPIs", "refId": "A", "round": "0s", "skip_comments": true @@ -394,7 +418,13 @@ "color": { "mode": "thresholds" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "DeprecatedAPIs", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -443,8 +473,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694243320) AND EventTime <= toDateTime(1694243620)", + "query": "SELECT count(*) FROM default.DeprecatedAPIs", + "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs", "refId": "A", "round": "0s", "skip_comments": true @@ -464,7 +494,13 @@ "color": { "mode": "thresholds" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Kubernetes Resources", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -513,8 +549,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT count(*) FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694243344) AND EventTime <= toDateTime(1694243644)", + "query": "SELECT count(*) FROM default.getall_resources", + "rawQuery": "SELECT count(*) FROM default.getall_resources", "refId": "A", "round": "0s", "skip_comments": true @@ -528,7 +564,7 @@ "type": "grafana-clickhouse-datasource", "uid": "ClickHouse" }, - "description": "This panel provides a time-based analysis of the occurrences of 'Pod' and 'Node'", + "description": "This panel provides a time-based analysis of the occurrences of 'Pod'", "fieldConfig": { "defaults": { "color": { @@ -587,7 +623,7 @@ }, "gridPos": { "h": 6, - "w": 24, + "w": 13, "x": 0, "y": 10 }, @@ -622,14 +658,96 @@ "rawSql": "SELECT EventTime, COUNT(*) AS Pods\nFROM default.events\nWHERE Kind = 'Pod'\nGROUP BY EventTime;\n", "refId": "A", "selectedFormat": 0 + } + ], + "title": "Number of Pods over time", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "description": "This panel provides a time-based analysis of the occurrences of 'Node'", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "yellow", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 13, + "y": 10 + }, + "id": 65, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ { "datasource": { "type": "grafana-clickhouse-datasource", "uid": "ClickHouse" }, "format": 0, - "hide": false, "meta": { "builderOptions": { "fields": [], @@ -639,11 +757,11 @@ }, "queryType": "sql", "rawSql": "SELECT EventTime, COUNT(*) AS Nodes\nFROM default.events\nWHERE Kind = 'Node'\nGROUP BY EventTime;\n", - "refId": "B", + "refId": "A", "selectedFormat": 0 } ], - "title": "Number of Pods and Nodes over time", + "title": "Number of Nodes over Time", "type": "timeseries" }, { @@ -658,7 +776,13 @@ "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -727,7 +851,13 @@ "description": "This panel displays the total number of pods with Created state.", "fieldConfig": { "defaults": { - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -798,7 +928,13 @@ "description": "This panel displays the total number of pods with backOff state.", "fieldConfig": { "defaults": { - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -869,7 +1005,13 @@ "description": "This panel displays the total number of pods with Unhealthy state.", "fieldConfig": { "defaults": { - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -944,7 +1086,13 @@ "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -1013,7 +1161,13 @@ "description": "This panel displays the total number of nodes which is in not ready state", "fieldConfig": { "defaults": { - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -1084,7 +1238,13 @@ "description": "This panel displays the total number of nodes which is in ready state", "fieldConfig": { "defaults": { - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -1155,7 +1315,13 @@ "description": "This panel displays the total number of nodes which is in NodeHasNoDiskPressure state", "fieldConfig": { "defaults": { - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "percentage", @@ -1237,7 +1403,13 @@ "filterable": true, "inspect": false }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Kubedata", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -1331,9 +1503,13 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total number of clusters containing DeletedAPIs activity.", + "description": "This panel displays the total number of clusters containing activity uniquely.", "fieldConfig": { "defaults": { + "color": { + "mode": "thresholds" + }, + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1342,13 +1518,9 @@ "color": "green", "value": null }, - { - "color": "orange", - "value": 70 - }, { "color": "red", - "value": 85 + "value": 80 } ] } @@ -1356,12 +1528,12 @@ "overrides": [] }, "gridPos": { - "h": 4, - "w": 6, + "h": 5, + "w": 24, "x": 0, "y": 32 }, - "id": 65, + "id": 40, "options": { "orientation": "auto", "reduceOptions": { @@ -1391,61 +1563,7 @@ "refId": "A", "round": "0s", "skip_comments": true - } - ], - "title": "Clusters Containing DeletedAPIs Activity", - "type": "gauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the total number of clusters containing Events activity.", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 70 - }, - { - "color": "red", - "value": 85 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 6, - "y": 32 - }, - "id": 67, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1455,67 +1573,14 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "refId": "A", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "refId": "B", "round": "0s", "skip_comments": true - } - ], - "title": "Clusters Containing Events Activity", - "type": "gauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the total number of clusters containing Outdated Images activity.", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 70 - }, - { - "color": "red", - "value": 85 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 12, - "y": 32 - }, - "id": 68, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1525,67 +1590,14 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "refId": "A", + "query": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", + "refId": "C", "round": "0s", "skip_comments": true - } - ], - "title": "Clusters Containing Outdated_Images Activity", - "type": "gauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the total number of clusters containing DeprecatedAPIs activity.", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 70 - }, - { - "color": "red", - "value": 85 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 18, - "y": 32 - }, - "id": 66, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1595,15 +1607,16 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "refId": "A", + "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "refId": "D", "round": "0s", "skip_comments": true } ], - "title": "Clusters Containing DeprecatedAPIs Activity", + "title": "Number of Clusters Containing Activity", "type": "gauge" }, { @@ -1625,7 +1638,13 @@ "filterable": true, "inspect": false }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "KubeData", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -1664,7 +1683,7 @@ "h": 8, "w": 24, "x": 0, - "y": 36 + "y": 37 }, "id": 64, "options": { @@ -1691,8 +1710,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", - "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE EventTime >= toDateTime(1694243396) AND EventTime <= toDateTime(1694243696) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", "refId": "A", "round": "0s", "skip_comments": true @@ -1734,7 +1753,13 @@ "mode": "off" } }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "DeprecatedAPIs", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -1756,7 +1781,7 @@ "h": 9, "w": 12, "x": 0, - "y": 44 + "y": 45 }, "id": 34, "options": { @@ -1791,8 +1816,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694243425) AND EventTime <= toDateTime(1694243725)\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1834,7 +1859,13 @@ "mode": "off" } }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "DeletedAPIs", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -1856,7 +1887,7 @@ "h": 9, "w": 12, "x": 12, - "y": 44 + "y": 45 }, "id": 36, "options": { @@ -1891,8 +1922,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694243449) AND EventTime <= toDateTime(1694243749)\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1934,7 +1965,13 @@ "mode": "off" } }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Outdated Images", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -1956,7 +1993,7 @@ "h": 8, "w": 8, "x": 0, - "y": 53 + "y": 54 }, "id": 28, "options": { @@ -1991,8 +2028,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694243473) AND EventTime <= toDateTime(1694243773)\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2033,7 +2070,13 @@ "mode": "off" } }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Kubedata", + "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -2055,7 +2098,7 @@ "h": 8, "w": 8, "x": 8, - "y": 53 + "y": 54 }, "id": 32, "options": { @@ -2133,7 +2176,13 @@ "mode": "off" } }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Kubernetes Resources", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -2155,7 +2204,7 @@ "h": 8, "w": 8, "x": 16, - "y": 53 + "y": 54 }, "id": 30, "options": { @@ -2190,8 +2239,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694200604) AND EventTime <= toDateTime(1694243804)\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2219,7 +2268,13 @@ "filterable": true, "inspect": false }, - "links": [], + "links": [ + { + "targetBlank": true, + "title": "Kubernetes Resources", + "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" + } + ], "mappings": [], "thresholds": { "mode": "absolute", @@ -2262,7 +2317,7 @@ "h": 7, "w": 24, "x": 0, - "y": 61 + "y": 62 }, "id": 42, "options": { @@ -2289,8 +2344,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", - "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694200646) AND EventTime <= toDateTime(1694243846)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -2309,7 +2364,7 @@ "h": 1, "w": 24, "x": 0, - "y": 68 + "y": 69 }, "id": 16, "panels": [ @@ -2337,8 +2392,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2353,7 +2407,7 @@ "h": 16, "w": 24, "x": 0, - "y": 69 + "y": 78 }, "id": 14, "options": { @@ -2380,14 +2434,15 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT * FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694200677) AND EventTime <= toDateTime(1694243877)", + "query": "SELECT * FROM default.getall_resources", + "rawQuery": "SELECT * FROM default.getall_resources", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Kubernetes Resources", + "transparent": true, "type": "table" } ], @@ -2413,7 +2468,7 @@ "h": 1, "w": 24, "x": 0, - "y": 69 + "y": 70 }, "id": 12, "panels": [ @@ -2440,8 +2495,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2456,13 +2510,11 @@ "h": 16, "w": 24, "x": 0, - "y": 70 + "y": 226 }, "id": 10, "options": { - "cellHeight": "sm", "footer": { - "countRows": false, "fields": "", "reducer": [ "sum" @@ -2471,7 +2523,7 @@ }, "showHeader": true }, - "pluginVersion": "10.0.3", + "pluginVersion": "9.3.2", "targets": [ { "datasource": { @@ -2483,14 +2535,15 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", - "rawQuery": "SELECT * FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694200706) AND EventTime <= toDateTime(1694243906) AND VersionsBehind > 0", + "query": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", + "rawQuery": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "Outdated Images", + "transparent": true, "type": "table" } ], @@ -2516,7 +2569,7 @@ "h": 1, "w": 24, "x": 0, - "y": 70 + "y": 71 }, "id": 8, "panels": [ @@ -2544,8 +2597,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2560,13 +2612,11 @@ "h": 11, "w": 24, "x": 0, - "y": 71 + "y": 227 }, "id": 6, "options": { - "cellHeight": "sm", "footer": { - "countRows": false, "fields": "", "reducer": [ "sum" @@ -2575,7 +2625,7 @@ }, "showHeader": true }, - "pluginVersion": "10.0.3", + "pluginVersion": "9.3.2", "targets": [ { "datasource": { @@ -2587,14 +2637,15 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT * FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694200743) AND EventTime <= toDateTime(1694243943)", + "query": "SELECT * FROM default.DeletedAPIs", + "rawQuery": "SELECT * FROM default.DeletedAPIs", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "DeletedAPIs", + "transparent": true, "type": "table" } ], @@ -2620,7 +2671,7 @@ "h": 1, "w": 24, "x": 0, - "y": 71 + "y": 72 }, "id": 4, "panels": [ @@ -2648,8 +2699,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2664,13 +2714,11 @@ "h": 8, "w": 24, "x": 0, - "y": 72 + "y": 228 }, "id": 2, "options": { - "cellHeight": "sm", "footer": { - "countRows": false, "fields": "", "reducer": [ "sum" @@ -2679,7 +2727,7 @@ }, "showHeader": true }, - "pluginVersion": "10.0.3", + "pluginVersion": "9.3.2", "targets": [ { "datasource": { @@ -2691,14 +2739,15 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT * FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694200768) AND EventTime <= toDateTime(1694243968)", + "query": "SELECT * FROM default.DeprecatedAPIs", + "rawQuery": "SELECT * FROM default.DeprecatedAPIs", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "DeprecatedAPIs", + "transparent": true, "type": "table" } ], @@ -2730,6 +2779,6 @@ "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 3, + "version": 1, "weekStart": "" } \ No newline at end of file From 8f851730165829a1a32a4823145a995cff4c212d Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Mon, 18 Sep 2023 10:02:37 +0530 Subject: [PATCH 041/263] Added trivy includes echarts panel --- grafana/trivy-dashboard.json | 398 ++++++++++++----------------------- 1 file changed, 137 insertions(+), 261 deletions(-) diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 45860fa3..7e1358b3 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -25,6 +25,124 @@ "links": [], "liveNow": false, "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 36, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'vul_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_severity", + "rawQuery": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694438766) AND vul_last_modified_date <= toDateTime(1694611566)\nGROUP BY vul_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Severity Distribution", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 37, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'misconfig_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY misconfig_severity", + "rawQuery": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694438912) AND EventTime <= toDateTime(1694611712)\nGROUP BY misconfig_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Severity Distribution", + "type": "volkovlabs-echarts-panel" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -57,7 +175,7 @@ "h": 8, "w": 12, "x": 0, - "y": 0 + "y": 8 }, "id": 20, "options": { @@ -127,7 +245,7 @@ "h": 8, "w": 12, "x": 12, - "y": 0 + "y": 8 }, "id": 22, "options": { @@ -198,7 +316,7 @@ "h": 5, "w": 12, "x": 0, - "y": 8 + "y": 16 }, "id": 18, "options": { @@ -266,7 +384,7 @@ "h": 5, "w": 12, "x": 12, - "y": 8 + "y": 16 }, "id": 16, "options": { @@ -333,7 +451,7 @@ "h": 7, "w": 12, "x": 0, - "y": 13 + "y": 21 }, "id": 29, "options": { @@ -403,7 +521,7 @@ "h": 7, "w": 12, "x": 12, - "y": 13 + "y": 21 }, "id": 30, "options": { @@ -473,7 +591,7 @@ "h": 7, "w": 12, "x": 0, - "y": 20 + "y": 28 }, "id": 27, "options": { @@ -543,7 +661,7 @@ "h": 7, "w": 12, "x": 12, - "y": 20 + "y": 28 }, "id": 28, "options": { @@ -613,7 +731,7 @@ "h": 7, "w": 12, "x": 0, - "y": 27 + "y": 35 }, "id": 25, "options": { @@ -683,7 +801,7 @@ "h": 7, "w": 12, "x": 12, - "y": 27 + "y": 35 }, "id": 26, "options": { @@ -753,7 +871,7 @@ "h": 7, "w": 12, "x": 0, - "y": 34 + "y": 42 }, "id": 12, "options": { @@ -823,7 +941,7 @@ "h": 7, "w": 12, "x": 12, - "y": 34 + "y": 42 }, "id": 14, "options": { @@ -862,248 +980,6 @@ "title": "Critical Misconfiguration Count by Namespace and ClusterName", "type": "bargauge" }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the total count of Vulnerability severity for each level", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 70 - }, - { - "color": "red", - "value": 85 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 12, - "x": 0, - "y": 41 - }, - "id": 10, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'", - "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'HIGH'", - "refId": "A", - "round": "0s", - "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'", - "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'MEDIUM'", - "refId": "B", - "round": "0s", - "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'", - "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'LOW'", - "refId": "C", - "round": "0s", - "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'", - "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156133) AND vul_last_modified_date <= toDateTime(1694242533) AND vul_severity = 'CRITICAL'", - "refId": "D", - "round": "0s", - "skip_comments": true - } - ], - "title": "Count of Vulnereability Severity level", - "type": "gauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the total count of Misconfiguration severity for each level", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "orange", - "value": 70 - }, - { - "color": "red", - "value": 85 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 12, - "x": 12, - "y": 41 - }, - "id": 8, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'", - "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'HIGH'", - "refId": "A", - "round": "0s", - "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'", - "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'MEDIUM'", - "refId": "B", - "round": "0s", - "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'", - "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'LOW'", - "refId": "C", - "round": "0s", - "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'", - "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156516) AND EventTime <= toDateTime(1694242916) AND misconfig_severity = 'CRITICAL'", - "refId": "D", - "round": "0s", - "skip_comments": true - } - ], - "title": "Count of Misconfiguration Severity Level", - "type": "gauge" - }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1136,7 +1012,7 @@ "h": 8, "w": 12, "x": 0, - "y": 47 + "y": 49 }, "id": 6, "options": { @@ -1206,7 +1082,7 @@ "h": 8, "w": 12, "x": 12, - "y": 47 + "y": 49 }, "id": 4, "options": { @@ -1279,7 +1155,7 @@ "h": 8, "w": 24, "x": 0, - "y": 55 + "y": 57 }, "id": 32, "options": { @@ -1351,7 +1227,7 @@ "h": 8, "w": 24, "x": 0, - "y": 63 + "y": 65 }, "id": 33, "options": { @@ -1423,7 +1299,7 @@ "h": 8, "w": 24, "x": 0, - "y": 71 + "y": 73 }, "id": 34, "options": { @@ -1495,7 +1371,7 @@ "h": 8, "w": 24, "x": 0, - "y": 79 + "y": 81 }, "id": 35, "options": { @@ -1548,6 +1424,6 @@ "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 2, + "version": 3, "weekStart": "" } \ No newline at end of file From 9d34e81d4fb8191273b2c479bd3e8b424f3fe809 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Sep 2023 15:59:11 +0530 Subject: [PATCH 042/263] test --- agent/config/config.go | 4 ++-- agent/kubviz/k8smetrics_agent.go | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/agent/config/config.go b/agent/config/config.go index 50209ccd..645b5364 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -8,13 +8,13 @@ import ( type AgentConfigurations struct { SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` SAName string `envconfig:"SA_NAME" default:"default"` - OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"*/20 * * * *"` + OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"@every 5m"` GetAllInterval string `envconfig:"GETALL_INTERVAL" default:"*/30 * * * *"` KubeScoreInterval string `envconfig:"KUBESCORE_INTERVAL" default:"*/40 * * * *"` RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` TrivyInterval string `envconfig:"TRIVY_INTERVAL" default:"*/10 * * * *"` - SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"false"` + SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"true"` } func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index a807bac2..b26c6c5b 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -118,17 +118,17 @@ func main() { collectAndPublishMetrics := func() { err := outDatedImages(config, js) LogErr(err) - err = KubePreUpgradeDetector(config, js) - LogErr(err) - err = GetAllResources(config, js) - LogErr(err) - err = RakeesOutput(config, js) - LogErr(err) - // getK8sEvents(clientset) - err = runTrivyScans(config, js) - LogErr(err) - err = RunKubeScore(clientset, js) - LogErr(err) + // err = KubePreUpgradeDetector(config, js) + // LogErr(err) + // err = GetAllResources(config, js) + // LogErr(err) + // err = RakeesOutput(config, js) + // LogErr(err) + // // getK8sEvents(clientset) + // err = runTrivyScans(config, js) + // LogErr(err) + // err = RunKubeScore(clientset, js) + // LogErr(err) } collectAndPublishMetrics() From 99dc6730bed73d8d9decb1a867ce6d2c45bc5adf Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Mon, 18 Sep 2023 20:01:54 +0530 Subject: [PATCH 043/263] Dashboard modifications --- grafana/gitHub-dashboard.json | 2 +- grafana/kubvizDsahboard.json | 166 ++++++++++++++++++++++++++-------- values.yaml | 13 +++ 3 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 values.yaml diff --git a/grafana/gitHub-dashboard.json b/grafana/gitHub-dashboard.json index 59a7cda2..c5f7100d 100644 --- a/grafana/gitHub-dashboard.json +++ b/grafana/gitHub-dashboard.json @@ -443,7 +443,7 @@ ] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index 1383336b..d34e499b 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, + "id": 76, "links": [], "liveNow": false, "panels": [ @@ -1503,24 +1503,27 @@ "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total number of clusters containing activity uniquely.", + "description": "This panel displays the total number of clusters containing DeletedAPIs activity.", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, - "links": [], "mappings": [], "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, + { + "color": "orange", + "value": 70 + }, { "color": "red", - "value": 80 + "value": 85 } ] } @@ -1529,11 +1532,11 @@ }, "gridPos": { "h": 5, - "w": 24, + "w": 8, "x": 0, "y": 32 }, - "id": 40, + "id": 66, "options": { "orientation": "auto", "reduceOptions": { @@ -1563,24 +1566,61 @@ "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Number of Clusters Containing DeletedAPIs Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the total number of clusters containing Outdated Images activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "refId": "B", - "round": "0s", - "skip_comments": true + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 32 + }, + "id": 68, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1590,14 +1630,67 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "refId": "C", + "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Number of Clusters Containing Outdated Images Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the total number of clusters containing DeprecatedAPIs activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 32 + }, + "id": 67, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1607,16 +1700,15 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "refId": "D", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Number of Clusters Containing Activity", + "title": "Number of Clusters Containing DeprecatedAPIs Activity", "type": "gauge" }, { @@ -2407,7 +2499,7 @@ "h": 16, "w": 24, "x": 0, - "y": 78 + "y": 102 }, "id": 14, "options": { @@ -2510,7 +2602,7 @@ "h": 16, "w": 24, "x": 0, - "y": 226 + "y": 250 }, "id": 10, "options": { @@ -2612,7 +2704,7 @@ "h": 11, "w": 24, "x": 0, - "y": 227 + "y": 251 }, "id": 6, "options": { @@ -2714,7 +2806,7 @@ "h": 8, "w": 24, "x": 0, - "y": 228 + "y": 252 }, "id": 2, "options": { @@ -2779,6 +2871,6 @@ "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 1, + "version": 2, "weekStart": "" } \ No newline at end of file diff --git a/values.yaml b/values.yaml new file mode 100644 index 00000000..a1cfac9e --- /dev/null +++ b/values.yaml @@ -0,0 +1,13 @@ +image: + repository: ghcr.io/intelops/kubviz/client + tag: "pr-237" + +migration: + image: + repository: ghcr.io/intelops/kubviz/migration + tag: "pr-237" + +nats: + auth: + enabled: true + token: "l8ZMOoWF2YZ7dlBtd92s28wQx4D7F35f" From 15515fff0dee23758cd8604079dd4ccccfdc7734 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Mon, 18 Sep 2023 21:45:41 +0530 Subject: [PATCH 044/263] Added time series in trivy_misconfig --- grafana/trivy-dashboard.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 7e1358b3..917c5b1c 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 7, + "id": 86, "links": [], "liveNow": false, "panels": [ @@ -1254,8 +1254,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694156566) AND EventTime <= toDateTime(1694242966)", + "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -1359,8 +1359,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } From 84284e0b74cf3d48a0514dbd9a0fc642ec8f46e1 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Mon, 18 Sep 2023 22:02:56 +0530 Subject: [PATCH 045/263] removed unwanted file --- values.yaml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 values.yaml diff --git a/values.yaml b/values.yaml deleted file mode 100644 index a1cfac9e..00000000 --- a/values.yaml +++ /dev/null @@ -1,13 +0,0 @@ -image: - repository: ghcr.io/intelops/kubviz/client - tag: "pr-237" - -migration: - image: - repository: ghcr.io/intelops/kubviz/migration - tag: "pr-237" - -nats: - auth: - enabled: true - token: "l8ZMOoWF2YZ7dlBtd92s28wQx4D7F35f" From b663347852d6378dc9b92af02ec11c7592a660fb Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 06:42:16 +0530 Subject: [PATCH 046/263] added helm values --- agent/kubviz/k8smetrics_agent.go | 22 +++++++++++----------- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 14 ++++++++++++++ charts/agent/values.yaml | 8 +++++++- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index b26c6c5b..a807bac2 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -118,17 +118,17 @@ func main() { collectAndPublishMetrics := func() { err := outDatedImages(config, js) LogErr(err) - // err = KubePreUpgradeDetector(config, js) - // LogErr(err) - // err = GetAllResources(config, js) - // LogErr(err) - // err = RakeesOutput(config, js) - // LogErr(err) - // // getK8sEvents(clientset) - // err = runTrivyScans(config, js) - // LogErr(err) - // err = RunKubeScore(clientset, js) - // LogErr(err) + err = KubePreUpgradeDetector(config, js) + LogErr(err) + err = GetAllResources(config, js) + LogErr(err) + err = RakeesOutput(config, js) + LogErr(err) + // getK8sEvents(clientset) + err = runTrivyScans(config, js) + LogErr(err) + err = RunKubeScore(clientset, js) + LogErr(err) } collectAndPublishMetrics() diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index c5769556..2d5827bd 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.0 +version: 1.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index 490049c8..94c592ca 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -54,6 +54,20 @@ spec: value: {{ .Values.nats.host }} - name: SCHEDULING_INTERVAL value: {{ .Values.schedulingInterval }} + - name: SCHEDULER_ENABLE + value: {{ .Values.schedulingEnable }} + - name: OUTDATED_INTERVAL + value: {{ .Values.OutdatedInterval }} + - name: GETALL_INTERVAL + value: {{ .Values.GetallInterval }} + - name: KUBESCORE_INTERVAL + value: {{ .Values.KubescoreInterval }} + - name: RAKKESS_INTERVAL + value: {{ .Values.RakkessInterval }} + - name: KUBEPREUPGRADE_INTERVAL + value: {{ .Values.KubepreupgradeInterval }} + - name: TRIVY_INTERVAL + value: {{ .Values.TrivyInterval }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- if .Values.git_bridge.enabled }} diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index cc67af35..7ea1a46f 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -143,7 +143,13 @@ tolerations: [] affinity: {} schedulingInterval: "24h" - +schedulingEnable: "false" +OutdatedInterval: "@every 18h" +GetallInterval: "@every 19h" +KubescoreInterval: "@every 20h" +RakkessInterval: "@every 21h" +KubepreupgradeInterval: "@every 22h" +TrivyInterval: "@every 24h" clusterName: "kubviz" nats: host: kubviz-client-nats From 82e9aff7b1eaef83bd02a105f7ee675fe8f83d13 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 06:43:56 +0530 Subject: [PATCH 047/263] added helm values --- charts/agent/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 7ea1a46f..447e1fb7 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -143,7 +143,7 @@ tolerations: [] affinity: {} schedulingInterval: "24h" -schedulingEnable: "false" +schedulingEnable: "fals" OutdatedInterval: "@every 18h" GetallInterval: "@every 19h" KubescoreInterval: "@every 20h" From 4de74f06585b33de1f7c66904ffc43b8b20e6d1e Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 06:45:06 +0530 Subject: [PATCH 048/263] added helm values --- agent/config/config.go | 2 +- charts/agent/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/config/config.go b/agent/config/config.go index 645b5364..737148bf 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -8,7 +8,7 @@ import ( type AgentConfigurations struct { SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` SAName string `envconfig:"SA_NAME" default:"default"` - OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"@every 5m"` + OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"@every 20m"` GetAllInterval string `envconfig:"GETALL_INTERVAL" default:"*/30 * * * *"` KubeScoreInterval string `envconfig:"KUBESCORE_INTERVAL" default:"*/40 * * * *"` RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 447e1fb7..7ea1a46f 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -143,7 +143,7 @@ tolerations: [] affinity: {} schedulingInterval: "24h" -schedulingEnable: "fals" +schedulingEnable: "false" OutdatedInterval: "@every 18h" GetallInterval: "@every 19h" KubescoreInterval: "@every 20h" From 663b6284dac27a9f8c28b467d04bd9fd328e4956 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 10:00:58 +0530 Subject: [PATCH 049/263] authchange --- agent/config/config.go | 2 +- client/pkg/clients/clients.go | 7 ++++++- client/pkg/config/config.go | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/agent/config/config.go b/agent/config/config.go index 737148bf..e97b3e11 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -14,7 +14,7 @@ type AgentConfigurations struct { RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` TrivyInterval string `envconfig:"TRIVY_INTERVAL" default:"*/10 * * * *"` - SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"true"` + SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"false"` } func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { diff --git a/client/pkg/clients/clients.go b/client/pkg/clients/clients.go index 0ac4fef2..83e5b526 100644 --- a/client/pkg/clients/clients.go +++ b/client/pkg/clients/clients.go @@ -21,7 +21,12 @@ func NewNATSContext(conf *config.Config, dbClient clickhouse.DBInterface) (*NATS log.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + // conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + // if err != nil { + // return nil, err + // } + + conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.AuthToken)) if err != nil { return nil, err } diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index b50c970e..98bd9000 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -5,4 +5,5 @@ type Config struct { NatsToken string `envconfig:"NATS_TOKEN"` DbPort int `envconfig:"DB_PORT"` DBAddress string `envconfig:"DB_ADDRESS"` + AuthToken string `envconfig:"AUTH_TOKEN"` } From 76a43ec1daf5342183f6589265ff6451200e4ae6 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 10:07:26 +0530 Subject: [PATCH 050/263] authchange --- agent/kubviz/k8smetrics_agent.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index a807bac2..1a038c75 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -53,7 +53,9 @@ const ( var ( ClusterName string = os.Getenv("CLUSTER_NAME") token string = os.Getenv("NATS_TOKEN") + authtoken string = os.Getenv("AUTH_TOKEN") natsurl string = os.Getenv("NATS_ADDRESS") + //for local testing provide the location of kubeconfig // inside the civo file paste your kubeconfig // uncomment this line from Dockerfile.Kubviz (COPY --from=builder /workspace/civo /etc/myapp/civo) @@ -91,7 +93,7 @@ func main() { clientset *kubernetes.Clientset ) // connecting with nats ... - nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(authtoken)) checkErr(err) // creating a jetstream connection using the nats connection js, err := nc.JetStream() From 8e006b4663db8d3de8166df1ef634d6682188786 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 10:19:30 +0530 Subject: [PATCH 051/263] authchange --- agent/container/pkg/clients/nats_client.go | 2 +- agent/container/pkg/config/configuration.go | 1 + agent/git/pkg/clients/nats_client.go | 2 +- agent/git/pkg/config/configuration.go | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/agent/container/pkg/clients/nats_client.go b/agent/container/pkg/clients/nats_client.go index f42185cc..e919ce64 100755 --- a/agent/container/pkg/clients/nats_client.go +++ b/agent/container/pkg/clients/nats_client.go @@ -41,7 +41,7 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { fmt.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.AuthToken)) if err != nil { return nil, err } diff --git a/agent/container/pkg/config/configuration.go b/agent/container/pkg/config/configuration.go index 6d5580b9..ce9ee973 100755 --- a/agent/container/pkg/config/configuration.go +++ b/agent/container/pkg/config/configuration.go @@ -7,6 +7,7 @@ package config type Config struct { NatsAddress string `envconfig:"NATS_ADDRESS"` NatsToken string `envconfig:"NATS_TOKEN"` + AuthToken string `envconfig:"AUTH_TOKEN"` Port int `envconfig:"PORT"` StreamName string `envconfig:"STREAM_NAME"` } diff --git a/agent/git/pkg/clients/nats_client.go b/agent/git/pkg/clients/nats_client.go index e9ef06c2..27ced09f 100644 --- a/agent/git/pkg/clients/nats_client.go +++ b/agent/git/pkg/clients/nats_client.go @@ -30,7 +30,7 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { fmt.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.AuthToken)) if err != nil { return nil, err } diff --git a/agent/git/pkg/config/configuration.go b/agent/git/pkg/config/configuration.go index f48954f8..273d5559 100644 --- a/agent/git/pkg/config/configuration.go +++ b/agent/git/pkg/config/configuration.go @@ -3,10 +3,11 @@ package config // var token string = "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" // var natsurl string = "nats://localhost:4222" -//Config will have the configuration details +// Config will have the configuration details type Config struct { NatsAddress string `envconfig:"NATS_ADDRESS"` NatsToken string `envconfig:"NATS_TOKEN"` + AuthToken string `envconfig:"AUTH_TOKEN"` Port int `envconfig:"PORT"` StreamName string `envconfig:"STREAM_NAME"` } From 1c8a15f780e44ab4cb30f2f1f6c7fabde173bf98 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 15:09:03 +0530 Subject: [PATCH 052/263] helm changes --- agent/container/pkg/clients/nats_client.go | 2 +- agent/container/pkg/config/configuration.go | 1 - agent/git/pkg/clients/nats_client.go | 2 +- agent/git/pkg/config/configuration.go | 1 - agent/kubviz/k8smetrics_agent.go | 6 +++--- charts/agent/templates/deployment.yaml | 14 +++++++------- charts/agent/values.yaml | 18 ++++++++++-------- client/pkg/clients/clients.go | 2 +- client/pkg/config/config.go | 1 - 9 files changed, 23 insertions(+), 24 deletions(-) diff --git a/agent/container/pkg/clients/nats_client.go b/agent/container/pkg/clients/nats_client.go index e919ce64..f42185cc 100755 --- a/agent/container/pkg/clients/nats_client.go +++ b/agent/container/pkg/clients/nats_client.go @@ -41,7 +41,7 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { fmt.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.AuthToken)) + conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) if err != nil { return nil, err } diff --git a/agent/container/pkg/config/configuration.go b/agent/container/pkg/config/configuration.go index ce9ee973..6d5580b9 100755 --- a/agent/container/pkg/config/configuration.go +++ b/agent/container/pkg/config/configuration.go @@ -7,7 +7,6 @@ package config type Config struct { NatsAddress string `envconfig:"NATS_ADDRESS"` NatsToken string `envconfig:"NATS_TOKEN"` - AuthToken string `envconfig:"AUTH_TOKEN"` Port int `envconfig:"PORT"` StreamName string `envconfig:"STREAM_NAME"` } diff --git a/agent/git/pkg/clients/nats_client.go b/agent/git/pkg/clients/nats_client.go index 27ced09f..e9ef06c2 100644 --- a/agent/git/pkg/clients/nats_client.go +++ b/agent/git/pkg/clients/nats_client.go @@ -30,7 +30,7 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { fmt.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.AuthToken)) + conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) if err != nil { return nil, err } diff --git a/agent/git/pkg/config/configuration.go b/agent/git/pkg/config/configuration.go index 273d5559..1a5d66a4 100644 --- a/agent/git/pkg/config/configuration.go +++ b/agent/git/pkg/config/configuration.go @@ -7,7 +7,6 @@ package config type Config struct { NatsAddress string `envconfig:"NATS_ADDRESS"` NatsToken string `envconfig:"NATS_TOKEN"` - AuthToken string `envconfig:"AUTH_TOKEN"` Port int `envconfig:"PORT"` StreamName string `envconfig:"STREAM_NAME"` } diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 1a038c75..81ca71d1 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -53,8 +53,8 @@ const ( var ( ClusterName string = os.Getenv("CLUSTER_NAME") token string = os.Getenv("NATS_TOKEN") - authtoken string = os.Getenv("AUTH_TOKEN") - natsurl string = os.Getenv("NATS_ADDRESS") + + natsurl string = os.Getenv("NATS_ADDRESS") //for local testing provide the location of kubeconfig // inside the civo file paste your kubeconfig @@ -93,7 +93,7 @@ func main() { clientset *kubernetes.Clientset ) // connecting with nats ... - nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(authtoken)) + nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) checkErr(err) // creating a jetstream connection using the nats connection js, err := nc.JetStream() diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index 94c592ca..407c35fc 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -53,19 +53,19 @@ spec: - name: NATS_ADDRESS value: {{ .Values.nats.host }} - name: SCHEDULING_INTERVAL - value: {{ .Values.schedulingInterval }} + value: {{ .Values.schedule.schedulingInterval }} - name: SCHEDULER_ENABLE - value: {{ .Values.schedulingEnable }} + value: {{ .Values.schedule.enabled }} - name: OUTDATED_INTERVAL - value: {{ .Values.OutdatedInterval }} + value: {{ .Values.schedule.outdatedInterval }} - name: GETALL_INTERVAL - value: {{ .Values.GetallInterval }} + value: {{ .Values.schedule.getallInterval }} - name: KUBESCORE_INTERVAL - value: {{ .Values.KubescoreInterval }} + value: {{ .Values.schedule.kubescoreInterval }} - name: RAKKESS_INTERVAL - value: {{ .Values.RakkessInterval }} + value: {{ .Values.schedule.rakkessInterval }} - name: KUBEPREUPGRADE_INTERVAL - value: {{ .Values.KubepreupgradeInterval }} + value: {{ .Values.schedule.kubepreupgradeInterval }} - name: TRIVY_INTERVAL value: {{ .Values.TrivyInterval }} resources: diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 7ea1a46f..dba580a7 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -142,14 +142,16 @@ tolerations: [] affinity: {} -schedulingInterval: "24h" -schedulingEnable: "false" -OutdatedInterval: "@every 18h" -GetallInterval: "@every 19h" -KubescoreInterval: "@every 20h" -RakkessInterval: "@every 21h" -KubepreupgradeInterval: "@every 22h" -TrivyInterval: "@every 24h" +schedule: + enabled: false + schedulingInterval: "24h" + outdatedInterval: "@every 18h" + getallInterval: "@every 19h" + kubescoreInterval: "@every 20h" + rakkessInterval: "@every 21h" + kubepreupgradeInterval: "@every 22h" + trivyInterval: "@every 24h" + clusterName: "kubviz" nats: host: kubviz-client-nats diff --git a/client/pkg/clients/clients.go b/client/pkg/clients/clients.go index 83e5b526..4dc23a9f 100644 --- a/client/pkg/clients/clients.go +++ b/client/pkg/clients/clients.go @@ -26,7 +26,7 @@ func NewNATSContext(conf *config.Config, dbClient clickhouse.DBInterface) (*NATS // return nil, err // } - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.AuthToken)) + conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) if err != nil { return nil, err } diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index 98bd9000..b50c970e 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -5,5 +5,4 @@ type Config struct { NatsToken string `envconfig:"NATS_TOKEN"` DbPort int `envconfig:"DB_PORT"` DBAddress string `envconfig:"DB_ADDRESS"` - AuthToken string `envconfig:"AUTH_TOKEN"` } From 90ed1d1aee9794b2b0c19cc5b3a48b7dbed3ccb9 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Sep 2023 15:11:25 +0530 Subject: [PATCH 053/263] helm changes --- client/pkg/clients/clients.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/pkg/clients/clients.go b/client/pkg/clients/clients.go index 4dc23a9f..0ac4fef2 100644 --- a/client/pkg/clients/clients.go +++ b/client/pkg/clients/clients.go @@ -21,11 +21,6 @@ func NewNATSContext(conf *config.Config, dbClient clickhouse.DBInterface) (*NATS log.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - // conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) - // if err != nil { - // return nil, err - // } - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) if err != nil { return nil, err From 432e9013ccc293f59b04869d6a40b4d9d10389fd Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 19 Sep 2023 19:34:36 +0530 Subject: [PATCH 054/263] Add secret reference for NATS token --- charts/nats/Chart.yaml | 2 +- charts/nats/templates/configmap.yaml | 14 +++++++------- charts/nats/templates/statefulset.yaml | 8 +++++++- charts/nats/values.yaml | 9 +++++++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/charts/nats/Chart.yaml b/charts/nats/Chart.yaml index 2ceaa087..ae0e67fa 100644 --- a/charts/nats/Chart.yaml +++ b/charts/nats/Chart.yaml @@ -19,4 +19,4 @@ maintainers: name: Jaime Piña url: https://github.com/variadico name: nats -version: 0.13.4 +version: 0.13.5 diff --git a/charts/nats/templates/configmap.yaml b/charts/nats/templates/configmap.yaml index ceb891ae..27c0ac37 100644 --- a/charts/nats/templates/configmap.yaml +++ b/charts/nats/templates/configmap.yaml @@ -482,11 +482,13 @@ data: system_account: {{ . }} {{- end }} - {{- with .Values.auth.token }} authorization { - token: "{{ . }}" - - + {{- if .Values.auth.token }} + token: "{{ .Values.auth.token }}" + {{- else if .Values.auth.secret }} + token: $AUTH_TOKEN + {{- end }} + {{- if $.Values.auth.timeout }} timeout: {{ $.Values.auth.timeout }} {{- end }} @@ -539,6 +541,4 @@ data: accounts: {{- toRawJson . }} {{- end }} - {{- end }} - - {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/nats/templates/statefulset.yaml b/charts/nats/templates/statefulset.yaml index 9533e499..2c90e60d 100644 --- a/charts/nats/templates/statefulset.yaml +++ b/charts/nats/templates/statefulset.yaml @@ -318,7 +318,6 @@ spec: fieldPath: metadata.namespace - name: CLUSTER_ADVERTISE value: {{ include "nats.clusterAdvertise" . }} - {{- if .Values.nats.jetstream.enabled }} {{- with .Values.nats.jetstream.encryption }} {{- with .secret }} @@ -330,6 +329,13 @@ spec: {{- end }} {{- end }} {{- end }} + {{- if and .Values.auth.enabled .Values.auth.secret }} + - name: AUTH_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.auth.secret.name }} + key: {{ .Values.auth.secret.key }} + {{- end }} volumeMounts: - name: config-volume mountPath: /etc/nats-config diff --git a/charts/nats/values.yaml b/charts/nats/values.yaml index b59003fd..e2839093 100644 --- a/charts/nats/values.yaml +++ b/charts/nats/values.yaml @@ -531,8 +531,13 @@ auth: # name: operator-jwt # key: KO.jwt - # Token authentication - # token: + # Use key if you want to provide the token via Helm Values + # token: + + # Use a secret reference if you want to get a token from a secret + # secret: + # name: "nats-token" + # key: "key" # NKey authentication # nkeys: From d0844b54df62d05ff0275904ada7a87f4854073f Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 19 Sep 2023 21:15:23 +0530 Subject: [PATCH 055/263] Added secret reference nats token for client and agent chart --- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 21 +++++++++++++++++++++ charts/agent/values.yaml | 7 ++++++- charts/client/Chart.yaml | 4 ++-- charts/client/templates/deployment.yaml | 9 ++++++++- charts/client/values.yaml | 7 ++++++- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 2d5827bd..939082d5 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.1 +version: 1.1.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index 407c35fc..dbbf85b7 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -49,7 +49,14 @@ spec: - name: CLUSTER_NAME value: {{ .Values.clusterName }} - name: NATS_TOKEN + {{- if .Values.nats.auth.token }} value: {{ .Values.nats.auth.token }} + {{- else if .Values.nats.auth.secret }} + valueFrom: + secretKeyRef: + name: {{ .Values.nats.auth.secret.name }} + key: {{ .Values.nats.auth.secret.key }} + {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} - name: SCHEDULING_INTERVAL @@ -82,7 +89,14 @@ spec: - name: CLUSTER_NAME value: {{ .Values.clusterName }} - name: NATS_TOKEN + {{- if .Values.nats.auth.token }} value: {{ .Values.nats.auth.token }} + {{- else if .Values.nats.auth.secret }} + valueFrom: + secretKeyRef: + name: {{ .Values.nats.auth.secret.name }} + key: {{ .Values.nats.auth.secret.key }} + {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} resources: @@ -100,7 +114,14 @@ spec: - name: CLUSTER_NAME value: {{ .Values.clusterName }} - name: NATS_TOKEN + {{- if .Values.nats.auth.token }} value: {{ .Values.nats.auth.token }} + {{- else if .Values.nats.auth.secret }} + valueFrom: + secretKeyRef: + name: {{ .Values.nats.auth.secret.name }} + key: {{ .Values.nats.auth.secret.key }} + {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} resources: diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index dba580a7..0787dc3a 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -156,4 +156,9 @@ clusterName: "kubviz" nats: host: kubviz-client-nats auth: - token: "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" + # Use token if you want to provide the token via Helm Values + token: "" + # Use a secret reference if you want to get a token from a secret + secret: + name: "" + key: "" diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 53bf6cf1..5b6b5071 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.1 +version: 1.1.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to @@ -25,7 +25,7 @@ appVersion: "v1.0.0" dependencies: - name: nats condition: nats.enabled - version: 0.13.4 + version: 0.13.5 repository: https://intelops.github.io/kubviz/ - name: clickhouse condition: clickhouse.enabled diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index 0fd4d7fd..d33e17ea 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -63,7 +63,14 @@ spec: # port: http env: - name: NATS_TOKEN + {{- if and .Values.nats.enabled .Values.nats.auth.enabled .Values.nats.auth.token }} value: {{ .Values.nats.auth.token }} + {{- else if and .Values.nats.enabled .Values.nats.auth.enabled .Values.nats.auth.secret }} + valueFrom: + secretKeyRef: + name: {{ .Values.nats.auth.secret.name }} + key: {{ .Values.nats.auth.secret.key }} + {{- end }} - name: NATS_ADDRESS value: {{ include "client.fullname" . }}-nats - name: DB_ADDRESS @@ -83,4 +90,4 @@ spec: {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/client/values.yaml b/charts/client/values.yaml index a4053a25..0d1ab1e7 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -83,7 +83,12 @@ nats: #Authentication setup auth: enabled: true - token: "UfmrJOYwYCCsgQvxvcfJ3BdI6c8WBbnD" + # Use token if you want to provide the token via Helm Values + token: "" + # Use a secret reference if you want to get a token from a secret + secret: + name: "" + key: "" nats: jetstream: enabled: true From 3657479d2bb0b0fbaa4849f02188b1808aa97f25 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 20 Sep 2023 18:51:48 +0530 Subject: [PATCH 056/263] fix: agent chart --- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 939082d5..e85bbae2 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.2 +version: 1.1.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index dbbf85b7..1ce92e1d 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -60,21 +60,21 @@ spec: - name: NATS_ADDRESS value: {{ .Values.nats.host }} - name: SCHEDULING_INTERVAL - value: {{ .Values.schedule.schedulingInterval }} + value: "{{ .Values.schedule.schedulingInterval }}" - name: SCHEDULER_ENABLE - value: {{ .Values.schedule.enabled }} + value: "{{ .Values.schedule.enabled }}" - name: OUTDATED_INTERVAL - value: {{ .Values.schedule.outdatedInterval }} + value: "{{ .Values.schedule.outdatedInterval }}" - name: GETALL_INTERVAL - value: {{ .Values.schedule.getallInterval }} + value: "{{ .Values.schedule.getallInterval }}" - name: KUBESCORE_INTERVAL - value: {{ .Values.schedule.kubescoreInterval }} + value: "{{ .Values.schedule.kubescoreInterval }}" - name: RAKKESS_INTERVAL - value: {{ .Values.schedule.rakkessInterval }} + value: "{{ .Values.schedule.rakkessInterval }}" - name: KUBEPREUPGRADE_INTERVAL - value: {{ .Values.schedule.kubepreupgradeInterval }} + value: "{{ .Values.schedule.kubepreupgradeInterval }}" - name: TRIVY_INTERVAL - value: {{ .Values.TrivyInterval }} + value: "{{ .Values.schedule.trivyInterval }}" resources: {{- toYaml .Values.resources | nindent 12 }} {{- if .Values.git_bridge.enabled }} From fd81b05240fad935c1329ac3a5178efd1e7c6f32 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Fri, 22 Sep 2023 14:49:19 +0530 Subject: [PATCH 057/263] modified dashboars --- grafana/azure-dashboard.json | 11 ++-- grafana/bitBucket-dashboard.json | 12 ++-- grafana/giTea-dashboard.json | 9 ++- grafana/gitHub-dashboard.json | 14 ++--- grafana/gitLab-dashboard.json | 12 ++-- grafana/kubeData-dashboard.json | 13 ++-- grafana/kubvizDsahboard.json | 104 +++++++++++++++++-------------- 7 files changed, 86 insertions(+), 89 deletions(-) diff --git a/grafana/azure-dashboard.json b/grafana/azure-dashboard.json index ed13de74..a634cc28 100644 --- a/grafana/azure-dashboard.json +++ b/grafana/azure-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 22, + "id": 12, "links": [], "liveNow": false, "panels": [ @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", "google": { "callback": "gmapReady", "key": "" @@ -473,7 +473,6 @@ "timezone": "", "title": "Azure", "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", - "version": 1, + "version": 2, "weekStart": "" -} - +} \ No newline at end of file diff --git a/grafana/bitBucket-dashboard.json b/grafana/bitBucket-dashboard.json index 43701d83..172b9d0b 100644 --- a/grafana/bitBucket-dashboard.json +++ b/grafana/bitBucket-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 24, + "id": 13, "links": [], "liveNow": false, "panels": [ @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", "google": { "callback": "gmapReady", "key": "" @@ -361,8 +361,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -475,5 +474,4 @@ "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", "version": 2, "weekStart": "" -} - +} \ No newline at end of file diff --git a/grafana/giTea-dashboard.json b/grafana/giTea-dashboard.json index 0b103eb3..5f21b3c7 100644 --- a/grafana/giTea-dashboard.json +++ b/grafana/giTea-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 23, + "id": 11, "links": [], "liveNow": false, "panels": [ @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", "google": { "callback": "gmapReady", "key": "" @@ -475,5 +475,4 @@ "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", "version": 2, "weekStart": "" -} - +} \ No newline at end of file diff --git a/grafana/gitHub-dashboard.json b/grafana/gitHub-dashboard.json index c5f7100d..71420e64 100644 --- a/grafana/gitHub-dashboard.json +++ b/grafana/gitHub-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 20, + "id": 10, "links": [], "liveNow": false, "panels": [ @@ -104,7 +104,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n right: '5%', // Adjust the right margin to position it on the top right\n top: '5%', // Adjust the top margin to position it on the top right\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n\n", "google": { "callback": "gmapReady", "key": "" @@ -163,7 +163,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -338,8 +338,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -450,7 +449,6 @@ "timezone": "", "title": "GitHub", "uid": "ef91218c-94cb-48b1-be1d-0bafe848b75c", - "version": 1, + "version": 2, "weekStart": "" -} - +} \ No newline at end of file diff --git a/grafana/gitLab-dashboard.json b/grafana/gitLab-dashboard.json index 986eb838..962b582e 100644 --- a/grafana/gitLab-dashboard.json +++ b/grafana/gitLab-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 21, + "id": 14, "links": [], "liveNow": false, "panels": [ @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n left: 'center',\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -361,8 +361,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -487,5 +486,4 @@ "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", "version": 2, "weekStart": "" -} - +} \ No newline at end of file diff --git a/grafana/kubeData-dashboard.json b/grafana/kubeData-dashboard.json index cf63956d..ce8dfaf6 100644 --- a/grafana/kubeData-dashboard.json +++ b/grafana/kubeData-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 19, + "id": 9, "links": [], "liveNow": false, "panels": [ @@ -50,7 +50,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract data from your JSON\nconst clusterNames = data.series[0].fields[0].values; // New column for ClusterName\nconst namespaces = data.series[0].fields[1].values;\nconst kinds = data.series[0].fields[2].values; // Adjusted index for Kind\nconst counts = data.series[0].fields[3].values; // New column for Count\n\n// Create a hierarchical structure from the data without a root node\nconst hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n};\n\nconst seenClusterNames = new Set();\nconst seenNamespaces = new Set();\n\nfor (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n}\n\n// Create the tree chart using ECharts\nconst option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 14, // Increase the text size for regular nodes\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 14, // Increase the text size for leaves\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n};\nreturn option;", + "getOption": "// Extract data from your JSON\nconst clusterNames = data.series[0].fields[0].values; // New column for ClusterName\nconst namespaces = data.series[0].fields[1].values;\nconst kinds = data.series[0].fields[2].values; // Adjusted index for Kind\nconst counts = data.series[0].fields[3].values; // New column for Count\n\n// Create a hierarchical structure from the data without a root node\nconst hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n};\n\nconst seenClusterNames = new Set();\nconst seenNamespaces = new Set();\n\nfor (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n}\n\n// Create the tree chart using ECharts\nconst option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n};\nreturn option;", "google": { "callback": "gmapReady", "key": "" @@ -297,11 +297,6 @@ "uid": "vertamedia-clickhouse-datasource" }, "definition": "SELECT Kind FROM default.events", - "error": { - "data": { - "message": "Unexpected error" - } - }, "hide": 0, "includeAll": true, "multi": true, @@ -347,6 +342,6 @@ "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 3, + "version": 2, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index d34e499b..b12ae28e 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 76, + "id": 8, "links": [], "liveNow": false, "panels": [ @@ -245,8 +245,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT count(*)\n\nFROM default.outdated_images\n\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", + "rawQuery": "SELECT count(*)\n\nFROM default.outdated_images\n\nWHERE EventTime >= toDateTime(1695283225) AND EventTime <= toDateTime(1695369625) AND VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true @@ -397,8 +397,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeletedAPIs", - "rawQuery": "SELECT count(*) FROM default.DeletedAPIs", + "query": "SELECT count(*) FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695367863) AND EventTime <= toDateTime(1695369663)", "refId": "A", "round": "0s", "skip_comments": true @@ -473,8 +473,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeprecatedAPIs", - "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs", + "query": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695367882) AND EventTime <= toDateTime(1695369682)", "refId": "A", "round": "0s", "skip_comments": true @@ -549,8 +549,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.getall_resources", - "rawQuery": "SELECT count(*) FROM default.getall_resources", + "query": "SELECT count(*) FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695367909) AND EventTime <= toDateTime(1695369709)", "refId": "A", "round": "0s", "skip_comments": true @@ -1561,8 +1561,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695369491) AND EventTime <= toDateTime(1695369791)", "refId": "A", "round": "0s", "skip_comments": true @@ -1631,8 +1631,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", + "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images\nWHERE EventTime >= toDateTime(1695369518) AND EventTime <= toDateTime(1695369818)", "refId": "A", "round": "0s", "skip_comments": true @@ -1701,8 +1701,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695369535) AND EventTime <= toDateTime(1695369835)", "refId": "A", "round": "0s", "skip_comments": true @@ -1802,8 +1802,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", - "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE EventTime >= toDateTime(1695369559) AND EventTime <= toDateTime(1695369859) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", "refId": "A", "round": "0s", "skip_comments": true @@ -1908,8 +1908,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695369580) AND EventTime <= toDateTime(1695369880)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2014,8 +2014,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695369607) AND EventTime <= toDateTime(1695369907)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2120,8 +2120,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE EventTime >= toDateTime(1695369627) AND EventTime <= toDateTime(1695369927)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2331,8 +2331,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695369653) AND EventTime <= toDateTime(1695369953)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2436,8 +2436,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", - "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695369674) AND EventTime <= toDateTime(1695369974)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -2484,7 +2484,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2499,7 +2500,7 @@ "h": 16, "w": 24, "x": 0, - "y": 102 + "y": 70 }, "id": 14, "options": { @@ -2526,8 +2527,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.getall_resources", - "rawQuery": "SELECT * FROM default.getall_resources", + "query": "SELECT * FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695369699) AND EventTime <= toDateTime(1695369999)", "refId": "A", "round": "0s", "skip_comments": true @@ -2587,7 +2588,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2602,11 +2604,13 @@ "h": 16, "w": 24, "x": 0, - "y": 250 + "y": 87 }, "id": 10, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2615,7 +2619,7 @@ }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2627,8 +2631,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT * FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", + "rawQuery": "SELECT * FROM default.outdated_images\nWHERE EventTime >= toDateTime(1695369722) AND EventTime <= toDateTime(1695370022) AND VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true @@ -2689,7 +2693,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2704,11 +2709,13 @@ "h": 11, "w": 24, "x": 0, - "y": 251 + "y": 72 }, "id": 6, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2717,7 +2724,7 @@ }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2729,8 +2736,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeletedAPIs", - "rawQuery": "SELECT * FROM default.DeletedAPIs", + "query": "SELECT * FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695369749) AND EventTime <= toDateTime(1695370049)", "refId": "A", "round": "0s", "skip_comments": true @@ -2791,7 +2798,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2806,11 +2814,13 @@ "h": 8, "w": 24, "x": 0, - "y": 252 + "y": 73 }, "id": 2, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2819,7 +2829,7 @@ }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2831,8 +2841,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeprecatedAPIs", - "rawQuery": "SELECT * FROM default.DeprecatedAPIs", + "query": "SELECT * FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695369773) AND EventTime <= toDateTime(1695370073)", "refId": "A", "round": "0s", "skip_comments": true @@ -2864,7 +2874,7 @@ "list": [] }, "time": { - "from": "now-24h", + "from": "now-5m", "to": "now" }, "timepicker": {}, From a9469e1eb945fc88e73e217847dd742c91994628 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Fri, 22 Sep 2023 19:47:21 +0530 Subject: [PATCH 058/263] Added dashboards --- charts/client/Chart.yaml | 2 +- .../templates/configmap-azure-dashboard.yaml | 490 ++++ .../configmap-bitbucket-dashboard.yaml | 489 ++++ .../configmap-containerbridge-dashboard.yaml | 341 +++ .../configmap-features-dashboard.yaml | 22 +- .../configmap-gitbridge-dashboard.yaml | 2237 ----------------- .../templates/configmap-gitea-dashboard.yaml | 490 ++++ .../templates/configmap-github-dashboard.yaml | 466 ++++ .../templates/configmap-gitlab-dashboard.yaml | 501 ++++ .../configmap-kubedata-dashboard.yaml | 70 +- .../configmap-kubescore-dashboard.yaml | 24 +- .../templates/configmap-kubviz-dashboard.yaml | 381 ++- .../templates/configmap-trivy-dashboard.yaml | 816 +++--- charts/client/values.yaml | 4 + 14 files changed, 3658 insertions(+), 2675 deletions(-) create mode 100644 charts/client/templates/configmap-azure-dashboard.yaml create mode 100644 charts/client/templates/configmap-bitbucket-dashboard.yaml create mode 100644 charts/client/templates/configmap-containerbridge-dashboard.yaml delete mode 100644 charts/client/templates/configmap-gitbridge-dashboard.yaml create mode 100644 charts/client/templates/configmap-gitea-dashboard.yaml create mode 100644 charts/client/templates/configmap-github-dashboard.yaml create mode 100644 charts/client/templates/configmap-gitlab-dashboard.yaml diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 5b6b5071..48aa34ec 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.2 +version: 1.1.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-azure-dashboard.yaml b/charts/client/templates/configmap-azure-dashboard.yaml new file mode 100644 index 00000000..8025b20a --- /dev/null +++ b/charts/client/templates/configmap-azure-dashboard.yaml @@ -0,0 +1,490 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-azure-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + azure.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 12, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"azure_devops\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"azure_devops\" \nWHERE TimeStamp >= toDateTime(1694534229) AND TimeStamp <= toDateTime(1694620629) AND EventType IN ('git.push') AND Author IN ('Anila Soman')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Contributions", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534188) AND TimeStamp <= toDateTime(1694620588) AND EventType = 'git.push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Azure push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.azure_devops \nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.azure_devops \nWHERE TimeStamp >= toDateTime(1694534208) AND TimeStamp <= toDateTime(1694620608) AND EventType = 'git.pullrequest.merged'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Azure Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-red", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.push'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534146) AND TimeStamp <= toDateTime(1694620546) AND EventType = 'git.push'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Push event counts", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-red", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'git.pullrequest.merged'", + "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534169) AND TimeStamp <= toDateTime(1694620569) AND EventType = 'git.pullrequest.merged'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Merge events count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.azure_devops\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)", + "rawQuery": "SELECT * FROM default.azure_devops\nWHERE TimeStamp >= toDateTime(1694534128) AND TimeStamp <= toDateTime(1694620528) AND EventType IN ('git.push') AND Author IN ('Anila Soman')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT EventType FROM default.azure_devops", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.azure_devops", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT Author FROM default.azure_devops", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.azure_devops", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Azure", + "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-bitbucket-dashboard.yaml b/charts/client/templates/configmap-bitbucket-dashboard.yaml new file mode 100644 index 00000000..caa52a35 --- /dev/null +++ b/charts/client/templates/configmap-bitbucket-dashboard.yaml @@ -0,0 +1,489 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-bitbucket-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + bitbucket.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 13, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"bitbucket\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType In ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"bitbucket\" \nWHERE TimeStamp >= toDateTime(1694536440) AND TimeStamp <= toDateTime(1694622840) AND EventType In ('repo:push','pullrequest:created','pullrequest:fulfilled') AND Author IN ('')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Events", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536383) AND TimeStamp <= toDateTime(1694622783) AND EventType = 'repo:push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of BitBucket Push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536421) AND TimeStamp <= toDateTime(1694622821) AND EventType = 'pullrequest:fulfilled'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of BitBucket Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'repo:push'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536343) AND TimeStamp <= toDateTime(1694622743) AND EventType = 'repo:push'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Push Events Count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pullrequest:fulfilled'", + "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536364) AND TimeStamp <= toDateTime(1694622764) AND EventType = 'pullrequest:fulfilled'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Merge events count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.bitbucket\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author In ($Author)", + "rawQuery": "SELECT * FROM default.bitbucket\nWHERE TimeStamp >= toDateTime(1694536323) AND TimeStamp <= toDateTime(1694622723) AND EventType IN ('repo:push','pullrequest:created','pullrequest:fulfilled') AND Author In ('')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "BitBucket Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT EventType FROM default.bitbucket", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.bitbucket", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT Author FROM default.bitbucket", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.bitbucket", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BitBucket", + "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-containerbridge-dashboard.yaml b/charts/client/templates/configmap-containerbridge-dashboard.yaml new file mode 100644 index 00000000..48a72bd8 --- /dev/null +++ b/charts/client/templates/configmap-containerbridge-dashboard.yaml @@ -0,0 +1,341 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-containerbridge-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + containerbridge.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 10, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.quaycontainerpush\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime", + "rawQuery": "SELECT * FROM default.quaycontainerpush\nWHERE EventTime >= toDateTime(1694165676) AND EventTime <= toDateTime(1694252076)\nORDER BY EventTime", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Quay Container Registry", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.jfrogcontainerpush\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.jfrogcontainerpush\nWHERE EventTime >= toDateTime(1694226084) AND EventTime <= toDateTime(1694247684)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "JFrog Container Registry", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.azurecontainerpush\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.azurecontainerpush\nWHERE EventTime >= toDateTime(1694165623) AND EventTime <= toDateTime(1694252023)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Azure Container Registry", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.dockerhubbuild\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC\n", + "rawQuery": "SELECT * FROM default.dockerhubbuild\nWHERE EventTime >= toDateTime(1694165564) AND EventTime <= toDateTime(1694251964)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "DockerHub", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Container-Bridge", + "uid": "cf8cf066-b241-48c8-9e3d-863eed33bcf3", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-features-dashboard.yaml b/charts/client/templates/configmap-features-dashboard.yaml index 481e2472..27ef8634 100644 --- a/charts/client/templates/configmap-features-dashboard.yaml +++ b/charts/client/templates/configmap-features-dashboard.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-features-dashboard + annotations: + grafana_folder: "Kubviz" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: @@ -30,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 5, + "id": 2, "links": [], "liveNow": false, "panels": [ @@ -101,8 +103,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.getall_resources", - "rawQuery": "SELECT * FROM default.getall_resources", + "query": "SELECT * FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.getall_resources\nWHERE EventTime >= toDateTime(1694219529) AND EventTime <= toDateTime(1694241129)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -178,8 +180,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT * FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.outdated_images\nWHERE EventTime >= toDateTime(1694219614) AND EventTime <= toDateTime(1694241214) AND VersionsBehind > 0\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -255,8 +257,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeletedAPIs", - "rawQuery": "SELECT * FROM default.DeletedAPIs", + "query": "SELECT * FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1694219743) AND EventTime <= toDateTime(1694241343)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -332,8 +334,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeprecatedAPIs", - "rawQuery": "SELECT * FROM default.DeprecatedAPIs", + "query": "SELECT * FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1694219780) AND EventTime <= toDateTime(1694241380)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -351,7 +353,7 @@ data: "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, diff --git a/charts/client/templates/configmap-gitbridge-dashboard.yaml b/charts/client/templates/configmap-gitbridge-dashboard.yaml deleted file mode 100644 index 39ed92d2..00000000 --- a/charts/client/templates/configmap-gitbridge-dashboard.yaml +++ /dev/null @@ -1,2237 +0,0 @@ -{{- if .Values.dashboards.enabled }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "client.fullname" . }}-gitbridge-dashboard - labels: - {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} -data: - gitbridge.json: |- - { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 9, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 0, - "y": 0 - }, - "id": 42, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITHUB Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "yellow", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 5, - "y": 0 - }, - "id": 43, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", - "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE EventType = 'Push Hook'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITLAB Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-blue", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 9, - "y": 0 - }, - "id": 44, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'repo:push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BITBUCKET Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "orange", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 14, - "y": 0 - }, - "id": 45, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", - "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE EventType = 'push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITEA Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-red", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 19, - "y": 0 - }, - "id": 46, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.push'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "AZURE Push events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 0, - "y": 5 - }, - "id": 37, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE EventType = 'pull_request'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITHUB Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "yellow", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 4, - "x": 5, - "y": 5 - }, - "id": 38, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", - "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE EventType = 'Merge Request Hook'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITLAB Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-blue", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 9, - "y": 5 - }, - "id": 39, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", - "rawQuery": "SELECT count(*) AS BitBucket FROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "BITBUCKET Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "orange", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 14, - "y": 5 - }, - "id": 40, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", - "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE EventType = 'pull_request'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "GITEA Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "light-red", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 19, - "y": 5 - }, - "id": 41, - "options": { - "colorMode": "background", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", - "rawQuery": "SELECT count(*) AS Azure FROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "AZURE Merge events", - "transparent": true, - "type": "stat" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of Push events on Azure by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#17bcc1", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 36, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Azure_Push_Events\nFROM default.azure_devops\nWHERE EventType = 'git.push'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "Azure Push Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of Merge events on Azure by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#17bcc1", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 10 - }, - "id": 34, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Azure_Merge_Events\nFROM default.azure_devops\nWHERE EventType = 'git.pullrequest.merged'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "Azure Merge Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of Push events on BitBucket by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#e8f808", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 18 - }, - "id": 28, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.bitbucket\nWHERE EventType = 'repo:push'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "BitBucket Push Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of Merge events on BitBucket by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#e8f808", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 18 - }, - "id": 30, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.bitbucket\nWHERE EventType = 'pullrequest:fulfilled'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "BitBucket Merge Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of Push events on GiTea by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#f46565", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 26 - }, - "id": 24, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE EventType = 'push'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GiTea Push Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of Merge events on GiTea by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "#f46565", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 26 - }, - "id": 26, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "builderOptions": { - "database": "default", - "fields": [], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [], - "table": "" - }, - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "database": "default", - "fields": [], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [], - "table": "" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE EventType = 'pull_request'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GiTea Merge Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of push events on GitLab by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 34 - }, - "id": 20, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE EventType = 'Push Hook'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GitLab Push Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of merge events on GitLab by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 34 - }, - "id": 22, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE EventType = 'Merge Request Hook'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GitLab Merge Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of push events on Github by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "left", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 44 - }, - "id": 16, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "table", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE EventType = 'push'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "Github Push Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "The panel displays the count of merge events on Github by each author. It provides insights into the contribution activity of different authors within the specified repository.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 44 - }, - "id": 18, - "options": { - "barRadius": 0.2, - "barWidth": 0.3, - "fullHighlight": false, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "auto", - "showValue": "always", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 0, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } - }, - "queryType": "sql", - "rawSql": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE EventType = 'pull_request'\nGROUP BY Author", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "Github Merge Events by Author", - "transparent": true, - "type": "barchart" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "This panel displays all the events occured in the Azure Procider", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 54 - }, - "id": 14, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "azure_devops" - }, - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"azure_devops\" ORDER BY TimeStamp DESC LIMIT 100", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "Azure", - "transparent": true, - "type": "table" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "This panel displays all the events occured in the BitBucket Providers", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 61 - }, - "id": 12, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "bitbucket" - }, - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"bitbucket\" ORDER BY TimeStamp DESC LIMIT 100", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "BitBucket", - "transparent": true, - "type": "table" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "This panel displays all the events occured in the GiTea provider", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 69 - }, - "id": 10, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "gitea" - }, - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"gitea\" ORDER BY TimeStamp DESC LIMIT 100", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GiTea", - "transparent": true, - "type": "table" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "This panel displays all the events occured in the GitLab provider.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 78 - }, - "id": 8, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "gitlab" - }, - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"gitlab\" ORDER BY TimeStamp DESC LIMIT 100", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GitLab", - "transparent": true, - "type": "table" - }, - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "description": "This panel displays all the events occured in the GitHub provider.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" - }, - "filterable": true, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 86 - }, - "id": 6, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ - { - "builderOptions": { - "database": "default", - "fields": [ - "Author", - "CommitID", - "CommitUrl", - "EventType", - "RepoName", - "TimeStamp" - ], - "filters": [], - "limit": 100, - "mode": "list", - "orderBy": [ - { - "dir": "DESC", - "name": "TimeStamp" - } - ], - "table": "github" - }, - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT Author, CommitID, CommitUrl, EventType, RepoName, TimeStamp FROM default.\"github\" ORDER BY TimeStamp DESC LIMIT 100", - "refId": "A", - "selectedFormat": 1 - } - ], - "title": "GitHub", - "transparent": true, - "type": "table" - } - ], - "refresh": "", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "GitBridge", - "uid": "u3EJcUqVk", - "version": 1, - "weekStart": "" - } -{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-gitea-dashboard.yaml b/charts/client/templates/configmap-gitea-dashboard.yaml new file mode 100644 index 00000000..43782f56 --- /dev/null +++ b/charts/client/templates/configmap-gitea-dashboard.yaml @@ -0,0 +1,490 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-gitea-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + gitea.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 11, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitea\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitea\" \nWHERE TimeStamp >= toDateTime(1694535218) AND TimeStamp <= toDateTime(1694621618) AND EventType IN ('push','pull_request') AND Author IN ('')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GiTea Contributions", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535157) AND TimeStamp <= toDateTime(1694621557) AND EventType = 'push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Gitea push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535179) AND TimeStamp <= toDateTime(1694621579) AND EventType = 'pull_request'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of Gitea Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", + "rawQuery": "SELECT count(*) AS GiTea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535111) AND TimeStamp <= toDateTime(1694621511) AND EventType = 'push'", + "refId": "A", + "round": "0s", + "skip_comments": false + } + ], + "title": "BitBucket Push event count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS Gitea FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535137) AND TimeStamp <= toDateTime(1694621537) AND EventType = 'pull_request'", + "refId": "A", + "round": "0s", + "skip_comments": false + } + ], + "title": "BitBucket Merge events count", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.gitea\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)", + "rawQuery": "SELECT * FROM default.gitea\nWHERE TimeStamp >= toDateTime(1694535089) AND TimeStamp <= toDateTime(1694621489) AND EventType IN ('push','pull_request') AND Author IN ('')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GiTea Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT EventType FROM default.gitea", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.gitea", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT Author FROM default.gitea", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.gitea", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "GiTea", + "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-github-dashboard.yaml b/charts/client/templates/configmap-github-dashboard.yaml new file mode 100644 index 00000000..12a5029d --- /dev/null +++ b/charts/client/templates/configmap-github-dashboard.yaml @@ -0,0 +1,466 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-github-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + github.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 10, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"github\" \nWHERE $timeFilterByColumn(TimeStamp) AND Author IN ($Author) AND EventType IN ($eventType)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"github\" \nWHERE TimeStamp >= toDateTime(1694597007) AND TimeStamp <= toDateTime(1694618607) AND Author IN ('ahinvinith') AND EventType IN ('push','pull_request')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitHub Contributors", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694596958) AND TimeStamp <= toDateTime(1694618558) AND EventType = 'push'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitHub push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.github\nWHERE TimeStamp >= toDateTime(1694596980) AND TimeStamp <= toDateTime(1694618580) AND EventType = 'pull_request'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitHub Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'pull_request'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694596901) AND TimeStamp <= toDateTime(1694618501) AND EventType = 'pull_request'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitHub Merge event Count", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GitHub FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'push'", + "rawQuery": "SELECT count(*) AS GitHub FROM default.github\nWHERE TimeStamp >= toDateTime(1694596932) AND TimeStamp <= toDateTime(1694618532) AND EventType = 'push'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitHub Push events count", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 25 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.github\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)\nORDER BY TimeStamp DESC", + "rawQuery": "SELECT * FROM default.github\nWHERE TimeStamp >= toDateTime(1694596869) AND TimeStamp <= toDateTime(1694618469) AND EventType IN ('push','pull_request') AND Author IN ('ahinvinith')\nORDER BY TimeStamp DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Github Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "select Author from default.github", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "select Author from default.github", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "select EventType from default.github", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "select EventType from default.github", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "GitHub", + "uid": "ef91218c-94cb-48b1-be1d-0bafe848b75c", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-gitlab-dashboard.yaml b/charts/client/templates/configmap-gitlab-dashboard.yaml new file mode 100644 index 00000000..e9b69191 --- /dev/null +++ b/charts/client/templates/configmap-gitlab-dashboard.yaml @@ -0,0 +1,501 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-gitlab-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + gitlab.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 14, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitlab\" \nWHERE $timeFilterByColumn(TimeStamp) AND EventType In ($eventType) AND Author IN ($Author)\nGROUP BY EventType, Author, RepoName", + "rawQuery": "SELECT EventType, Author, RepoName , count(*) AS Total\nFROM \"default\".\"gitlab\" \nWHERE TimeStamp >= toDateTime(1694533326) AND TimeStamp <= toDateTime(1694619726) AND EventType In ('Push Hook') AND Author IN ('Ahin Vinith')\nGROUP BY EventType, Author, RepoName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Contributions", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Push_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533277) AND TimeStamp <= toDateTime(1694619677) AND EventType = 'Push Hook'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitLab Push events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'\nGROUP BY Author", + "rawQuery": "SELECT Author, count(*) AS Merge_Events\nFROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533298) AND TimeStamp <= toDateTime(1694619698) AND EventType = 'Merge Request Hook'\nGROUP BY Author", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Number of GitHub Merge events grouped by author", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Push Hook'", + "rawQuery": "SELECT count(*) Gitlab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533230) AND TimeStamp <= toDateTime(1694619630) AND EventType = 'Push Hook'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Push Events", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType = 'Merge Request Hook'", + "rawQuery": "SELECT count(*) AS GitLab FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533256) AND TimeStamp <= toDateTime(1694619656) AND EventType = 'Merge Request Hook'", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Merge events", + "type": "stat" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 1, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT * FROM default.gitlab\nWHERE $timeFilterByColumn(TimeStamp) AND EventType IN ($eventType) AND Author IN ($Author)", + "rawQuery": "SELECT * FROM default.gitlab\nWHERE TimeStamp >= toDateTime(1694533200) AND TimeStamp <= toDateTime(1694619600) AND EventType IN ('Push Hook') AND Author IN ('Ahin Vinith')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "GitLab Events", + "type": "table" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT EventType FROM default.gitlab", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "eventType", + "options": [], + "query": "SELECT EventType FROM default.gitlab", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "definition": "SELECT Author FROM default.gitlab", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "Author", + "options": [], + "query": "SELECT Author FROM default.gitlab", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "GitLab", + "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubedata-dashboard.yaml b/charts/client/templates/configmap-kubedata-dashboard.yaml index af1e7eff..ff4b9e7c 100644 --- a/charts/client/templates/configmap-kubedata-dashboard.yaml +++ b/charts/client/templates/configmap-kubedata-dashboard.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-kubedatas-dashboard + annotations: + grafana_folder: "Kubviz" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: @@ -30,10 +32,69 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, + "id": 9, "links": [], "liveNow": false, "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Extract data from your JSON\nconst clusterNames = data.series[0].fields[0].values; // New column for ClusterName\nconst namespaces = data.series[0].fields[1].values;\nconst kinds = data.series[0].fields[2].values; // Adjusted index for Kind\nconst counts = data.series[0].fields[3].values; // New column for Count\n\n// Create a hierarchical structure from the data without a root node\nconst hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n};\n\nconst seenClusterNames = new Set();\nconst seenNamespaces = new Set();\n\nfor (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n}\n\n// Create the tree chart using ECharts\nconst option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n};\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Namespace, Kind, count(*) AS count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Namespace IN ($namespace)\nGROUP BY ClusterName, Namespace, Kind", + "rawQuery": "SELECT ClusterName, Namespace, Kind, count(*) AS count\nFROM default.events\nWHERE EventTime >= toDateTime(1694603943) AND EventTime <= toDateTime(1694604243) AND ClusterName IN ('dev') AND Namespace IN ('kubviz','argocd','observability','default','tracetestdemo','sonarqube','kube-system','tek','quality','tekton-pipelines','sample','tekton-pipelines-resolvers','tekton-chains','cert-manager','qtapp','otel-collector','mysql','traefik')\nGROUP BY ClusterName, Namespace, Kind", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Kubernetes Workload", + "type": "volkovlabs-echarts-panel" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -67,7 +128,7 @@ data: "h": 7, "w": 24, "x": 0, - "y": 0 + "y": 11 }, "id": 4, "options": { @@ -146,7 +207,7 @@ data: "h": 16, "w": 24, "x": 0, - "y": 7 + "y": 18 }, "id": 2, "options": { @@ -181,7 +242,6 @@ data: } ], "title": "Kubernetes", - "transparent": true, "type": "table" } ], @@ -293,7 +353,7 @@ data: "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 1, + "version": 2, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubescore-dashboard.yaml b/charts/client/templates/configmap-kubescore-dashboard.yaml index 2f80dd51..398a44c9 100644 --- a/charts/client/templates/configmap-kubescore-dashboard.yaml +++ b/charts/client/templates/configmap-kubescore-dashboard.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-kubescore-dashboard + annotations: + grafana_folder: "Kubviz" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: @@ -30,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 10, + "id": 5, "links": [], "liveNow": false, "panels": [ @@ -47,8 +49,11 @@ data: }, "custom": { "align": "center", - "displayMode": "color-text", - "filterable": true + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], "thresholds": { @@ -86,7 +91,7 @@ data: }, "showHeader": true }, - "pluginVersion": "8.4.6", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -98,34 +103,33 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.kubescore", - "rawQuery": "SELECT * FROM default.kubescore", + "query": "SELECT * FROM default.kubescore\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT * FROM default.kubescore\nWHERE EventTime >= toDateTime(1694245574) AND EventTime <= toDateTime(1694267174)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true } ], "title": "KubeScore", - "transparent": true, "type": "table" } ], "refresh": "", - "schemaVersion": 35, + "schemaVersion": 38, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "KubeScore", "uid": "d8f0fceb-7621-45bc-9710-89e11fe57a79", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubviz-dashboard.yaml b/charts/client/templates/configmap-kubviz-dashboard.yaml index c2700f00..eec05960 100644 --- a/charts/client/templates/configmap-kubviz-dashboard.yaml +++ b/charts/client/templates/configmap-kubviz-dashboard.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-kubviz-dashboard + annotations: + grafana_folder: "Kubviz" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: @@ -30,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, + "id": 8, "links": [], "liveNow": false, "panels": [ @@ -254,8 +256,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT count(*) FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT count(*)\n\nFROM default.outdated_images\n\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", + "rawQuery": "SELECT count(*)\n\nFROM default.outdated_images\n\nWHERE EventTime >= toDateTime(1695283225) AND EventTime <= toDateTime(1695369625) AND VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true @@ -406,8 +408,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeletedAPIs", - "rawQuery": "SELECT count(*) FROM default.DeletedAPIs", + "query": "SELECT count(*) FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695367863) AND EventTime <= toDateTime(1695369663)", "refId": "A", "round": "0s", "skip_comments": true @@ -482,8 +484,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.DeprecatedAPIs", - "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs", + "query": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695367882) AND EventTime <= toDateTime(1695369682)", "refId": "A", "round": "0s", "skip_comments": true @@ -558,8 +560,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) FROM default.getall_resources", - "rawQuery": "SELECT count(*) FROM default.getall_resources", + "query": "SELECT count(*) FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(*) FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695367909) AND EventTime <= toDateTime(1695369709)", "refId": "A", "round": "0s", "skip_comments": true @@ -573,7 +575,7 @@ data: "type": "grafana-clickhouse-datasource", "uid": "ClickHouse" }, - "description": "This panel provides a time-based analysis of the occurrences of 'Pod' and 'Node'", + "description": "This panel provides a time-based analysis of the occurrences of 'Pod'", "fieldConfig": { "defaults": { "color": { @@ -632,7 +634,7 @@ data: }, "gridPos": { "h": 6, - "w": 24, + "w": 13, "x": 0, "y": 10 }, @@ -667,14 +669,96 @@ data: "rawSql": "SELECT EventTime, COUNT(*) AS Pods\nFROM default.events\nWHERE Kind = 'Pod'\nGROUP BY EventTime;\n", "refId": "A", "selectedFormat": 0 + } + ], + "title": "Number of Pods over time", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "description": "This panel provides a time-based analysis of the occurrences of 'Node'", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "yellow", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 11, + "x": 13, + "y": 10 + }, + "id": 65, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ { "datasource": { "type": "grafana-clickhouse-datasource", "uid": "ClickHouse" }, "format": 0, - "hide": false, "meta": { "builderOptions": { "fields": [], @@ -684,11 +768,11 @@ data: }, "queryType": "sql", "rawSql": "SELECT EventTime, COUNT(*) AS Nodes\nFROM default.events\nWHERE Kind = 'Node'\nGROUP BY EventTime;\n", - "refId": "B", + "refId": "A", "selectedFormat": 0 } ], - "title": "Number of Pods and Nodes over time", + "title": "Number of Nodes over Time", "type": "timeseries" }, { @@ -1430,24 +1514,27 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total number of clusters containing activity uniquely.", + "description": "This panel displays the total number of clusters containing DeletedAPIs activity.", "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, - "links": [], "mappings": [], "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, + { + "color": "orange", + "value": 70 + }, { "color": "red", - "value": 80 + "value": 85 } ] } @@ -1456,11 +1543,11 @@ data: }, "gridPos": { "h": 5, - "w": 24, + "w": 8, "x": 0, "y": 32 }, - "id": 40, + "id": 66, "options": { "orientation": "auto", "reduceOptions": { @@ -1485,29 +1572,66 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeletedAPIs\nFROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695369491) AND EventTime <= toDateTime(1695369791)", "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Number of Clusters Containing DeletedAPIs Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel displays the total number of clusters containing Outdated Images activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs", - "refId": "B", - "round": "0s", - "skip_comments": true + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 32 + }, + "id": 68, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1517,14 +1641,67 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS Events\nFROM default.events", - "refId": "C", + "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images\nWHERE EventTime >= toDateTime(1695369518) AND EventTime <= toDateTime(1695369818)", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Number of Clusters Containing Outdated Images Activity", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel displays the total number of clusters containing DeprecatedAPIs activity.", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 32 + }, + "id": 67, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -1534,16 +1711,15 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "rawQuery": "SELECT count(DISTINCT(ClusterName)) AS OutdatedImages\nFROM default.outdated_images", - "refId": "D", + "query": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT COUNT(DISTINCT ClusterName) AS DeprecatedAPIs\nFROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695369535) AND EventTime <= toDateTime(1695369835)", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Number of Clusters Containing Activity", + "title": "Number of Clusters Containing DeprecatedAPIs Activity", "type": "gauge" }, { @@ -1577,7 +1753,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1636,8 +1813,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", - "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "query": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", + "rawQuery": "SELECT ClusterName, Reason, count(Kind) AS Pods FROM default.events\nWHERE EventTime >= toDateTime(1695369559) AND EventTime <= toDateTime(1695369859) AND Kind IN 'Pod' \nGROUP BY ClusterName,Reason", "refId": "A", "round": "0s", "skip_comments": true @@ -1691,7 +1868,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1741,8 +1919,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deprecated) AS DeprecatedAPIs FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695369580) AND EventTime <= toDateTime(1695369880)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1796,7 +1974,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1846,8 +2025,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Deleted) AS DeletedAPIs FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695369607) AND EventTime <= toDateTime(1695369907)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -1901,7 +2080,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1951,8 +2131,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(CurrentImage) AS Outdated_Images FROM default.outdated_images\nWHERE EventTime >= toDateTime(1695369627) AND EventTime <= toDateTime(1695369927)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2005,7 +2185,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2110,7 +2291,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2160,8 +2342,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", - "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName", + "query": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName", + "rawQuery": "SELECT ClusterName, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695369653) AND EventTime <= toDateTime(1695369953)\nGROUP BY ClusterName", "refId": "A", "round": "0s", "skip_comments": true @@ -2201,7 +2383,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "light-yellow", @@ -2264,8 +2447,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", - "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "query": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", + "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695369674) AND EventTime <= toDateTime(1695369974)\nGROUP BY ClusterName,Kind\nORDER BY Resources DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -2278,7 +2461,7 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 1, @@ -2312,7 +2495,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2354,8 +2538,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.getall_resources", - "rawQuery": "SELECT * FROM default.getall_resources", + "query": "SELECT * FROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.getall_resources\nWHERE EventTime >= toDateTime(1695369699) AND EventTime <= toDateTime(1695369999)", "refId": "A", "round": "0s", "skip_comments": true @@ -2370,7 +2554,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "refId": "A" } @@ -2382,7 +2566,7 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 1, @@ -2415,7 +2599,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2430,11 +2615,13 @@ data: "h": 16, "w": 24, "x": 0, - "y": 218 + "y": 87 }, "id": 10, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2443,7 +2630,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2455,8 +2642,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", - "rawQuery": "SELECT * FROM default.outdated_images\nWHERE VersionsBehind > 0", + "query": "SELECT * FROM default.outdated_images\nWHERE $timeFilterByColumn(EventTime) AND VersionsBehind > 0", + "rawQuery": "SELECT * FROM default.outdated_images\nWHERE EventTime >= toDateTime(1695369722) AND EventTime <= toDateTime(1695370022) AND VersionsBehind > 0", "refId": "A", "round": "0s", "skip_comments": true @@ -2471,7 +2658,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "refId": "A" } @@ -2483,7 +2670,7 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 1, @@ -2517,7 +2704,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2532,11 +2720,13 @@ data: "h": 11, "w": 24, "x": 0, - "y": 219 + "y": 72 }, "id": 6, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2545,7 +2735,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2557,8 +2747,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeletedAPIs", - "rawQuery": "SELECT * FROM default.DeletedAPIs", + "query": "SELECT * FROM default.DeletedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.DeletedAPIs\nWHERE EventTime >= toDateTime(1695369749) AND EventTime <= toDateTime(1695370049)", "refId": "A", "round": "0s", "skip_comments": true @@ -2573,7 +2763,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "refId": "A" } @@ -2585,7 +2775,7 @@ data: "collapsed": true, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 1, @@ -2619,7 +2809,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -2634,11 +2825,13 @@ data: "h": 8, "w": 24, "x": 0, - "y": 220 + "y": 73 }, "id": 2, "options": { + "cellHeight": "sm", "footer": { + "countRows": false, "fields": "", "reducer": [ "sum" @@ -2647,7 +2840,7 @@ data: }, "showHeader": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.0.3", "targets": [ { "datasource": { @@ -2659,8 +2852,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.DeprecatedAPIs", - "rawQuery": "SELECT * FROM default.DeprecatedAPIs", + "query": "SELECT * FROM default.DeprecatedAPIs\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT * FROM default.DeprecatedAPIs\nWHERE EventTime >= toDateTime(1695369773) AND EventTime <= toDateTime(1695370073)", "refId": "A", "round": "0s", "skip_comments": true @@ -2675,7 +2868,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "e06865c2-5bcc-4533-8de7-880298c555af" + "uid": "{{ .Values.datasources.uid }}" }, "refId": "A" } @@ -2692,14 +2885,14 @@ data: "list": [] }, "time": { - "from": "now-30m", + "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 4, + "version": 2, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 1b1dcc41..434329d9 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -3,6 +3,8 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-trivy-dashboard + annotations: + grafana_folder: "Kubviz" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: @@ -30,10 +32,128 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 4, + "id": 86, "links": [], "liveNow": false, "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 36, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'vul_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_severity", + "rawQuery": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694438766) AND vul_last_modified_date <= toDateTime(1694611566)\nGROUP BY vul_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Severity Distribution", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 37, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'misconfig_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY misconfig_severity", + "rawQuery": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694438912) AND EventTime <= toDateTime(1694611712)\nGROUP BY misconfig_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Severity Distribution", + "type": "volkovlabs-echarts-panel" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -66,7 +186,7 @@ data: "h": 8, "w": 12, "x": 0, - "y": 0 + "y": 8 }, "id": 20, "options": { @@ -94,8 +214,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nGROUP BY cluster_name, vul_severity", - "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nGROUP BY cluster_name, vul_severity", + "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, vul_severity", + "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155614) AND vul_last_modified_date <= toDateTime(1694242014)\nGROUP BY cluster_name, vul_severity", "refId": "A", "round": "0s", "skip_comments": true @@ -136,7 +256,7 @@ data: "h": 8, "w": 12, "x": 12, - "y": 0 + "y": 8 }, "id": 22, "options": { @@ -164,8 +284,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nGROUP BY cluster_name, misconfig_severity", - "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nGROUP BY cluster_name, misconfig_severity", + "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, misconfig_severity", + "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156239) AND EventTime <= toDateTime(1694242639)\nGROUP BY cluster_name, misconfig_severity", "refId": "A", "round": "0s", "skip_comments": true @@ -179,7 +299,7 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total of misconfigurations from each clusters.", + "description": "This panel displays the total number Vulnerabilities under each namespace", "fieldConfig": { "defaults": { "mappings": [], @@ -207,9 +327,9 @@ data: "h": 5, "w": 12, "x": 0, - "y": 8 + "y": 16 }, - "id": 16, + "id": 18, "options": { "orientation": "auto", "reduceOptions": { @@ -232,14 +352,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nGROUP BY cluster_name", + "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155665) AND vul_last_modified_date <= toDateTime(1694242065)\nGROUP BY cluster_name", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Count by Cluster", + "title": "Vulnerability Count by Cluster", "type": "gauge" }, { @@ -247,7 +367,7 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total number Vulnerabilities under each namespace", + "description": "This panel displays the total of misconfigurations from each clusters.", "fieldConfig": { "defaults": { "mappings": [], @@ -275,9 +395,9 @@ data: "h": 5, "w": 12, "x": 12, - "y": 8 + "y": 16 }, - "id": 18, + "id": 16, "options": { "orientation": "auto", "reduceOptions": { @@ -300,14 +420,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nGROUP BY cluster_name", + "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156266) AND EventTime <= toDateTime(1694242666)\nGROUP BY cluster_name", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Count by Cluster", + "title": "Misconfiguration Count by Cluster", "type": "gauge" }, { @@ -315,7 +435,7 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "description": "This panel provides a count of high vulnerabilities categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { @@ -342,9 +462,9 @@ data: "h": 7, "w": 12, "x": 0, - "y": 13 + "y": 21 }, - "id": 12, + "id": 29, "options": { "displayMode": "basic", "minVizHeight": 10, @@ -370,14 +490,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155706) AND vul_last_modified_date <= toDateTime(1694242106) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Critical Vulnerability Count by Namespace and ClusterName", + "title": "High Vulnerability Count by Namespace and ClusterName", "type": "bargauge" }, { @@ -385,7 +505,7 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of critical misconfigurations categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "description": "This panel provides a count of high misconfigurations categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { @@ -412,9 +532,9 @@ data: "h": 7, "w": 12, "x": 12, - "y": 13 + "y": 21 }, - "id": 14, + "id": 30, "options": { "displayMode": "basic", "minVizHeight": 10, @@ -439,16 +559,15 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "interval": "", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156303) AND EventTime <= toDateTime(1694242703) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Critical Misconfiguration Count by Namespace and ClusterName", + "title": "High Misconfiguration Count by Namespace and ClusterName", "type": "bargauge" }, { @@ -456,24 +575,23 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total count of Misconfiguration severity for each level", + "description": "This panel provides a count of Low vulnerabilities categorized by namespace. It helps to monitor and prioritize Low security issues across different namespaces.", "fieldConfig": { "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, "mappings": [], "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, - { - "color": "orange", - "value": 70 - }, { "color": "red", - "value": 85 + "value": 80 } ] } @@ -481,23 +599,24 @@ data: "overrides": [] }, "gridPos": { - "h": 6, + "h": 7, "w": 12, "x": 0, - "y": 20 + "y": 28 }, - "id": 8, + "id": 27, "options": { - "orientation": "auto", + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": [], "fields": "", - "values": false + "values": true }, - "showThresholdLabels": false, - "showThresholdMarkers": true + "showUnfilled": true, + "valueMode": "color" }, "pluginVersion": "10.0.3", "targets": [ @@ -511,29 +630,66 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'HIGH'", - "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'HIGH'", + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155745) AND vul_last_modified_date <= toDateTime(1694242145) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + } + ], + "title": "Low Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of low misconfigurations categorized by namespace. It helps to monitor and prioritize low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'MEDIUM'", - "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'MEDIUM'", - "refId": "B", - "round": "0s", - "skip_comments": true + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 28 + }, + "id": 28, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -543,14 +699,67 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'LOW'", - "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'LOW'", - "refId": "C", + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156331) AND EventTime <= toDateTime(1694242731) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Low Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of Medium vulnerabilities categorized by namespace. It helps to monitor and prioritize Medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 35 + }, + "id": 25, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -560,41 +769,39 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'", - "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE misconfig_severity = 'CRITICAL'", - "refId": "D", + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155776) AND vul_last_modified_date <= toDateTime(1694242176) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Count of Misconfiguration Severity Level", - "type": "gauge" + "title": "Medium Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total count of Vulnerability severity for each level", + "description": "This panel provides a count of medium misconfigurations categorized by namespace. It helps to monitor and prioritize medium security issues across different namespaces.", "fieldConfig": { "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, "mappings": [], "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, - { - "color": "orange", - "value": 70 - }, { "color": "red", - "value": 85 + "value": 80 } ] } @@ -602,23 +809,24 @@ data: "overrides": [] }, "gridPos": { - "h": 6, + "h": 7, "w": 12, "x": 12, - "y": 20 + "y": 35 }, - "id": 10, + "id": 26, "options": { - "orientation": "auto", + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": [], "fields": "", - "values": false + "values": true }, - "showThresholdLabels": false, - "showThresholdMarkers": true + "showUnfilled": true, + "valueMode": "color" }, "pluginVersion": "10.0.3", "targets": [ @@ -632,29 +840,66 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'HIGH'", - "rawQuery": "SELECT count(*) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'HIGH'", + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156357) AND EventTime <= toDateTime(1694242757) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + } + ], + "title": "Medium Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, - "intervalFactor": 1, - "query": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'MEDIUM'", - "rawQuery": "SELECT count(*) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'MEDIUM'", - "refId": "B", - "round": "0s", - "skip_comments": true + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 42 + }, + "id": 12, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -664,14 +909,67 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, "intervalFactor": 1, - "query": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'LOW'", - "rawQuery": "SELECT count(*) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'LOW'", - "refId": "C", + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156016) AND vul_last_modified_date <= toDateTime(1694242416) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "refId": "A", "round": "0s", "skip_comments": true + } + ], + "title": "Critical Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of critical misconfigurations categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 42 + }, + "id": 14, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -681,24 +979,24 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "hide": false, + "interval": "", "intervalFactor": 1, - "query": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'", - "rawQuery": "SELECT count(*) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_severity = 'CRITICAL'", - "refId": "D", + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156383) AND EventTime <= toDateTime(1694242783) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Count of Vulnereability Severity level", - "type": "gauge" + "title": "Critical Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", + "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", "fieldConfig": { "defaults": { "color": { @@ -725,9 +1023,9 @@ data: "h": 8, "w": 12, "x": 0, - "y": 26 + "y": 49 }, - "id": 4, + "id": 6, "options": { "displayMode": "lcd", "minVizHeight": 10, @@ -753,14 +1051,14 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156175) AND vul_last_modified_date <= toDateTime(1694242575)\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Count by Cluster and Namespace", + "title": "Vulnerability Count by Cluster and Namespace", "type": "bargauge" }, { @@ -768,7 +1066,7 @@ data: "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", + "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", "fieldConfig": { "defaults": { "color": { @@ -795,9 +1093,9 @@ data: "h": 8, "w": 12, "x": 12, - "y": 26 + "y": 49 }, - "id": 6, + "id": 4, "options": { "displayMode": "lcd", "minVizHeight": 10, @@ -823,20 +1121,20 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156545) AND EventTime <= toDateTime(1694242945)\nGROUP BY cluster_name, namespace", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Count by Cluster and Namespace", + "title": "Misconfiguration Count by Cluster and Namespace", "type": "bargauge" }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, "fieldConfig": { "defaults": { @@ -858,10 +1156,6 @@ data: { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -872,9 +1166,9 @@ data: "h": 8, "w": 24, "x": 0, - "y": 34 + "y": 57 }, - "id": 2, + "id": 32, "options": { "cellHeight": "sm", "footer": { @@ -890,40 +1184,20 @@ data: "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "cluster_name", - "namespace", - "kind", - "name", - "vul_id", - "vul_vendor_ids", - "vul_pkg_id", - "vul_pkg_name", - "vul_pkg_path", - "vul_installed_version", - "vul_fixed_version", - "vul_title", - "vul_severity", - "vul_published_date", - "vul_last_modified_date" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivy_vul" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" FROM \"default\".\"trivy_vul\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE vul_last_modified_date >= toDateTime(1694099993) AND vul_last_modified_date <= toDateTime(1694186393)\nORDER BY vul_last_modified_date DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy Vulnerabilities", @@ -931,10 +1205,9 @@ data: }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, - "description": "", "fieldConfig": { "defaults": { "color": { @@ -955,10 +1228,6 @@ data: { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -966,12 +1235,12 @@ data: "overrides": [] }, "gridPos": { - "h": 10, + "h": 8, "w": 24, "x": 0, - "y": 42 + "y": 65 }, - "id": 1, + "id": 33, "options": { "cellHeight": "sm", "footer": { @@ -987,39 +1256,20 @@ data: "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "cluster_name", - "namespace", - "kind", - "name", - "misconfig_id", - "misconfig_avdid", - "misconfig_type", - "misconfig_title", - "misconfig_desc", - "misconfig_msg", - "misconfig_query", - "misconfig_resolution", - "misconfig_severity", - "misconfig_status" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivy_misconfig" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" FROM \"default\".\"trivy_misconfig\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy Misconfiguration", @@ -1027,8 +1277,8 @@ data: }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, "fieldConfig": { "defaults": { @@ -1050,10 +1300,6 @@ data: { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -1061,12 +1307,12 @@ data: "overrides": [] }, "gridPos": { - "h": 10, + "h": 8, "w": 24, "x": 0, - "y": 52 + "y": 73 }, - "id": 24, + "id": 34, "options": { "cellHeight": "sm", "footer": { @@ -1082,79 +1328,20 @@ data: "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "0": "T", - "1": "h", - "2": "e", - "3": " ", - "4": "q", - "5": "u", - "6": "e", - "7": "r", - "8": "y", - "9": " ", - "10": "i", - "11": "s", - "12": " ", - "13": "n", - "14": "o", - "15": "t", - "16": " ", - "17": "a", - "18": " ", - "19": "s", - "20": "e", - "21": "l", - "22": "e", - "23": "c", - "24": "t", - "25": " ", - "26": "s", - "27": "t", - "28": "a", - "29": "t", - "30": "e", - "31": "m", - "32": "e", - "33": "n", - "34": "t", - "35": ".", - "database": "default", - "fields": [ - "cluster_name", - "artifact_name", - "vul_id", - "vul_pkg_id", - "vul_pkg_name", - "vul_installed_version", - "vul_fixed_version", - "vul_title", - "vul_severity", - "vul_published_date", - "vul_last_modified_date" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivyimage" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" - }, - "format": 1, - "meta": { - "builderOptions": { - "fields": [], - "limit": 100, - "mode": "list" - } + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, - "queryType": "builder", - "rawSql": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" FROM \"default\".\"trivyimage\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy Image", @@ -1162,8 +1349,8 @@ data: }, { "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, "fieldConfig": { "defaults": { @@ -1183,8 +1370,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -1195,9 +1381,9 @@ data: "h": 8, "w": 24, "x": 0, - "y": 62 + "y": 81 }, - "id": 25, + "id": 35, "options": { "cellHeight": "sm", "footer": { @@ -1213,26 +1399,20 @@ data: "pluginVersion": "10.0.3", "targets": [ { - "builderOptions": { - "database": "default", - "fields": [ - "*" - ], - "filters": [], - "limit": null, - "mode": "list", - "orderBy": [], - "table": "trivysbom" - }, "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": "ClickHouse" + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, - "format": 1, - "queryType": "builder", - "rawSql": "SELECT * FROM \"default\".\"trivysbom\"", + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE $timeFilterByColumn(metadata_timestamp)\nORDER BY metadata_timestamp DESC", + "rawQuery": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE metadata_timestamp >= toDateTime(1693581733) AND metadata_timestamp <= toDateTime(1694186533)\nORDER BY metadata_timestamp DESC", "refId": "A", - "selectedFormat": 1 + "round": "0s", + "skip_comments": true } ], "title": "Trivy_SBOM", @@ -1247,14 +1427,14 @@ data: "list": [] }, "time": { - "from": "now-6h", + "from": "now-24h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Trivy", - "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d47", - "version": 1, + "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", + "version": 3, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 0d1ab1e7..75137351 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -100,6 +100,10 @@ clickhouse: grafana: enabled: false + plugins: + - vertamedia-clickhouse-datasource + - grafana-clickhouse-datasource + - volkovlabs-echarts-panel dashboards: enabled: true From cec7d9efa1f4996983f5123a81120a5292b61eb3 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sat, 23 Sep 2023 16:13:35 +0530 Subject: [PATCH 059/263] updated values.yaml --- charts/client/Chart.yaml | 2 +- charts/client/values.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 48aa34ec..7d410ec0 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.3 +version: 1.1.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 75137351..32f4fde7 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -86,9 +86,9 @@ nats: # Use token if you want to provide the token via Helm Values token: "" # Use a secret reference if you want to get a token from a secret - secret: - name: "" - key: "" + # secret: + # name: "" + # key: "" nats: jetstream: enabled: true From f336adb12f38bbd2b7cd6cfe1323fd258bb316da Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 17:45:19 +0530 Subject: [PATCH 060/263] added time --- agent/kubviz/trivy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 46248c8a..55fab4e0 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -30,7 +30,7 @@ func executeCommandTrivy(command string) ([]byte, error) { } func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport - cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 20m -f json --cache-dir /tmp/.cache --debug" + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir /tmp/.cache --debug" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) From 2ad76f00a305a450fd9f13488c4f1f57f2e26094 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 19:35:20 +0530 Subject: [PATCH 061/263] fix --- agent/kubviz/trivy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 55fab4e0..34731c69 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -30,7 +30,7 @@ func executeCommandTrivy(command string) ([]byte, error) { } func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport - cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir /tmp/.cache --debug" + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 100m -f json --cache-dir /tmp/.cache" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) From 53b9800324b8e6d121b7ab579e10a171b92b7f0d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 20:25:02 +0530 Subject: [PATCH 062/263] fix --- agent/kubviz/k8smetrics_agent.go | 38 ++++++++++++++++---------------- agent/kubviz/trivy.go | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 81ca71d1..8cef0799 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -68,14 +68,14 @@ func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { if err != nil { return err } - err = RunTrivyImageScans(config, js) - if err != nil { - return err - } - err = RunTrivySbomScan(config, js) - if err != nil { - return err - } + // err = RunTrivyImageScans(config, js) + // if err != nil { + // return err + // } + // err = RunTrivySbomScan(config, js) + // if err != nil { + // return err + // } return nil } @@ -118,19 +118,19 @@ func main() { go publishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { - err := outDatedImages(config, js) - LogErr(err) - err = KubePreUpgradeDetector(config, js) - LogErr(err) - err = GetAllResources(config, js) - LogErr(err) - err = RakeesOutput(config, js) - LogErr(err) + // err := outDatedImages(config, js) + // LogErr(err) + // err = KubePreUpgradeDetector(config, js) + // LogErr(err) + // err = GetAllResources(config, js) + // LogErr(err) + // err = RakeesOutput(config, js) + // LogErr(err) // getK8sEvents(clientset) - err = runTrivyScans(config, js) - LogErr(err) - err = RunKubeScore(clientset, js) + err := runTrivyScans(config, js) LogErr(err) + // err = RunKubeScore(clientset, js) + // LogErr(err) } collectAndPublishMetrics() diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 34731c69..7de646b8 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -30,7 +30,7 @@ func executeCommandTrivy(command string) ([]byte, error) { } func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport - cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 100m -f json --cache-dir /tmp/.cache" + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 40m -f json --cache-dir /tmp/.cache" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) From 495d761d1ef86739ce30b34e5dd29f921092d64d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 22:16:17 +0530 Subject: [PATCH 063/263] fix --- agent/kubviz/trivy.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 7de646b8..20bb0f15 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -15,12 +15,23 @@ import ( ) func executeCommandTrivy(command string) ([]byte, error) { + ulimitCmd := exec.Command("/bin/sh", "-c", "ulimit -t 5 -v 500000") + var ulimitOut, ulimitErr bytes.Buffer + ulimitCmd.Stdout = &ulimitOut + ulimitCmd.Stderr = &ulimitErr + + err := ulimitCmd.Run() + + if err != nil { + log.Println("Execute ulimit Command Error", err.Error()) + return nil, err + } cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc cmd.Stderr = &errc - err := cmd.Run() + err = cmd.Run() if err != nil { log.Println("Execute Trivy Command Error", err.Error()) From 66ddff876966a1a5fb3f929daa4c3cc2cb999b7f Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 22:25:53 +0530 Subject: [PATCH 064/263] fix --- agent/kubviz/trivy.go | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 20bb0f15..7de646b8 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -15,23 +15,12 @@ import ( ) func executeCommandTrivy(command string) ([]byte, error) { - ulimitCmd := exec.Command("/bin/sh", "-c", "ulimit -t 5 -v 500000") - var ulimitOut, ulimitErr bytes.Buffer - ulimitCmd.Stdout = &ulimitOut - ulimitCmd.Stderr = &ulimitErr - - err := ulimitCmd.Run() - - if err != nil { - log.Println("Execute ulimit Command Error", err.Error()) - return nil, err - } cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc cmd.Stderr = &errc - err = cmd.Run() + err := cmd.Run() if err != nil { log.Println("Execute Trivy Command Error", err.Error()) From dccf98d4912679f848c8c138201def04db0f4c3f Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 22:38:47 +0530 Subject: [PATCH 065/263] fix --- agent/kubviz/trivy.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 7de646b8..20bb0f15 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -15,12 +15,23 @@ import ( ) func executeCommandTrivy(command string) ([]byte, error) { + ulimitCmd := exec.Command("/bin/sh", "-c", "ulimit -t 5 -v 500000") + var ulimitOut, ulimitErr bytes.Buffer + ulimitCmd.Stdout = &ulimitOut + ulimitCmd.Stderr = &ulimitErr + + err := ulimitCmd.Run() + + if err != nil { + log.Println("Execute ulimit Command Error", err.Error()) + return nil, err + } cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc cmd.Stderr = &errc - err := cmd.Run() + err = cmd.Run() if err != nil { log.Println("Execute Trivy Command Error", err.Error()) From b6e92f8c6bf79eec04d963cc7cca42f4195ff598 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sat, 23 Sep 2023 23:37:18 +0530 Subject: [PATCH 066/263] fix --- agent/kubviz/trivy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 20bb0f15..5e240daf 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -15,7 +15,7 @@ import ( ) func executeCommandTrivy(command string) ([]byte, error) { - ulimitCmd := exec.Command("/bin/sh", "-c", "ulimit -t 5 -v 500000") + ulimitCmd := exec.Command("/bin/sh", "-c", "ulimit -t unlimited -v unlimited") var ulimitOut, ulimitErr bytes.Buffer ulimitCmd.Stdout = &ulimitOut ulimitCmd.Stderr = &ulimitErr From 3ccc8e78da725da53edbd44a91f30bfe168dad82 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sun, 24 Sep 2023 06:56:20 +0530 Subject: [PATCH 067/263] fix --- agent/kubviz/trivy.go | 63 ++++++++++++++++++++++++++++--------------- model/trivy.go | 8 +++--- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 5e240daf..e8395cdf 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "compress/gzip" "encoding/json" "log" exec "os/exec" @@ -15,23 +16,12 @@ import ( ) func executeCommandTrivy(command string) ([]byte, error) { - ulimitCmd := exec.Command("/bin/sh", "-c", "ulimit -t unlimited -v unlimited") - var ulimitOut, ulimitErr bytes.Buffer - ulimitCmd.Stdout = &ulimitOut - ulimitCmd.Stderr = &ulimitErr - - err := ulimitCmd.Run() - - if err != nil { - log.Println("Execute ulimit Command Error", err.Error()) - return nil, err - } cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc cmd.Stderr = &errc - err = cmd.Run() + err := cmd.Run() if err != nil { log.Println("Execute Trivy Command Error", err.Error()) @@ -39,6 +29,21 @@ func executeCommandTrivy(command string) ([]byte, error) { return outc.Bytes(), err } + +// Compress data using gzip +func compressData(data []byte) ([]byte, error) { + var compressedData bytes.Buffer + gz := gzip.NewWriter(&compressedData) + _, err := gz.Write(data) + if err != nil { + return nil, err + } + if err := gz.Close(); err != nil { + return nil, err + } + return compressedData.Bytes(), nil +} + func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 40m -f json --cache-dir /tmp/.cache" @@ -70,24 +75,40 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { log.Printf("Error occurred while Unmarshalling json for k8s cluster scan: %v", err) return err } - err = publishTrivyK8sReport(report, js) + + // Compress the Trivy scan report data + compressedReport, err := compressData([]byte(jsonPart)) + if err != nil { + log.Printf("Error compressing Trivy scan report: %v", err) + return err + } + + // Create a new TrivyReport struct with all the data + trivyReport := model.Trivy{ + ID: uuid.New().String(), + ClusterName: ClusterName, + Report: report, + CompressedReport: compressedReport, + UncompressedReport: []byte(jsonPart), + } + + // Publish the TrivyReport + err = publishTrivyK8sReport(trivyReport, js) if err != nil { return err } return nil } -func publishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamContext) error { - metrics := model.Trivy{ - ID: uuid.New().String(), - ClusterName: ClusterName, - Report: report, - } - metricsJson, _ := json.Marshal(metrics) +func publishTrivyK8sReport(trivyReport model.Trivy, js nats.JetStreamContext) error { + // Create a JSON message for the TrivyReport + metricsJson, _ := json.Marshal(trivyReport) + + // Publish the JSON message to the specified NATS subject _, err := js.Publish(constants.TRIVY_K8S_SUBJECT, metricsJson) if err != nil { return err } - log.Printf("Trivy k8s cluster report with ID:%s has been published\n", metrics.ID) + log.Printf("Trivy k8s cluster report with ID:%s has been published\n", trivyReport.ID) return nil } diff --git a/model/trivy.go b/model/trivy.go index f7d564da..f031c5c3 100644 --- a/model/trivy.go +++ b/model/trivy.go @@ -3,7 +3,9 @@ package model import "github.com/aquasecurity/trivy/pkg/k8s/report" type Trivy struct { - ID string - ClusterName string - Report report.ConsolidatedReport + ID string + ClusterName string + Report report.ConsolidatedReport + CompressedReport []byte + UncompressedReport []byte } From c2ddbe1924c656258418fcf931e802e32359477e Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sun, 24 Sep 2023 09:44:03 +0530 Subject: [PATCH 068/263] fix --- agent/kubviz/trivy.go | 54 ++++++++++--------------------------------- 1 file changed, 12 insertions(+), 42 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index e8395cdf..75351981 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "compress/gzip" "encoding/json" "log" exec "os/exec" @@ -29,24 +28,9 @@ func executeCommandTrivy(command string) ([]byte, error) { return outc.Bytes(), err } - -// Compress data using gzip -func compressData(data []byte) ([]byte, error) { - var compressedData bytes.Buffer - gz := gzip.NewWriter(&compressedData) - _, err := gz.Write(data) - if err != nil { - return nil, err - } - if err := gz.Close(); err != nil { - return nil, err - } - return compressedData.Bytes(), nil -} - func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport - cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 40m -f json --cache-dir /tmp/.cache" + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 20m -f json --cache-dir /tmp/.cache --debug" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) @@ -57,6 +41,8 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { // Handle errors and process the command output as needed if err != nil { log.Printf("Error executing command: %v\n", err) + log.Printf("Command output: %s\n", out) + } // Log the command output for debugging purposes log.Printf("Command output: %s\n", out) @@ -75,40 +61,24 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { log.Printf("Error occurred while Unmarshalling json for k8s cluster scan: %v", err) return err } - - // Compress the Trivy scan report data - compressedReport, err := compressData([]byte(jsonPart)) - if err != nil { - log.Printf("Error compressing Trivy scan report: %v", err) - return err - } - - // Create a new TrivyReport struct with all the data - trivyReport := model.Trivy{ - ID: uuid.New().String(), - ClusterName: ClusterName, - Report: report, - CompressedReport: compressedReport, - UncompressedReport: []byte(jsonPart), - } - - // Publish the TrivyReport - err = publishTrivyK8sReport(trivyReport, js) + err = publishTrivyK8sReport(report, js) if err != nil { return err } return nil } -func publishTrivyK8sReport(trivyReport model.Trivy, js nats.JetStreamContext) error { - // Create a JSON message for the TrivyReport - metricsJson, _ := json.Marshal(trivyReport) - - // Publish the JSON message to the specified NATS subject +func publishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamContext) error { + metrics := model.Trivy{ + ID: uuid.New().String(), + ClusterName: ClusterName, + Report: report, + } + metricsJson, _ := json.Marshal(metrics) _, err := js.Publish(constants.TRIVY_K8S_SUBJECT, metricsJson) if err != nil { return err } - log.Printf("Trivy k8s cluster report with ID:%s has been published\n", trivyReport.ID) + log.Printf("Trivy k8s cluster report with ID:%s has been published\n", metrics.ID) return nil } From 7a22dc22f6a79c97c8b9f7bb7e1c99988481f90c Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sun, 24 Sep 2023 10:24:46 +0530 Subject: [PATCH 069/263] fix --- agent/kubviz/trivy.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 75351981..517544e4 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -5,6 +5,7 @@ import ( "encoding/json" "log" exec "os/exec" + "runtime/debug" "strings" "github.com/aquasecurity/trivy/pkg/k8s/report" @@ -42,6 +43,7 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { if err != nil { log.Printf("Error executing command: %v\n", err) log.Printf("Command output: %s\n", out) + log.Printf("Stack trace: %v\n", string(debug.Stack())) } // Log the command output for debugging purposes From 9606e7498f57a11d7df972700538b46c16f974af Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sun, 24 Sep 2023 17:57:52 +0530 Subject: [PATCH 070/263] fix --- agent/kubviz/trivy.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 517544e4..29af2f1e 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -4,9 +4,12 @@ import ( "bytes" "encoding/json" "log" + "os" exec "os/exec" + "os/signal" "runtime/debug" "strings" + "syscall" "github.com/aquasecurity/trivy/pkg/k8s/report" "github.com/google/uuid" @@ -29,13 +32,21 @@ func executeCommandTrivy(command string) ([]byte, error) { return outc.Bytes(), err } +func cleanup() { + log.Println("Performing cleanup...") + // Add your cleanup logic here +} + func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 20m -f json --cache-dir /tmp/.cache --debug" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) + sigCh := make(chan os.Signal, 1) + // Register the signals to handle + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) // Execute the command out, err := executeCommandTrivy(cmdString) @@ -67,6 +78,15 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { if err != nil { return err } + go func() { + sig := <-sigCh + switch sig { + case syscall.SIGINT, syscall.SIGTERM: + log.Printf("Received termination signal: %v\n", sig) + cleanup() // Perform any necessary cleanup + os.Exit(1) // Exit with a non-zero status code + } + }() return nil } From c927268ce556d860f75273f0d2c648497120c6fc Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Mon, 25 Sep 2023 06:56:10 +0530 Subject: [PATCH 071/263] newrelease --- .readme_assets/GitBridgeNew.jpeg | Bin 0 -> 136730 bytes .readme_assets/depricatedAPINew.jpeg | Bin 0 -> 163626 bytes .readme_assets/kubeDataNew.jpeg | Bin 0 -> 140556 bytes .readme_assets/sbom.jpeg | Bin 0 -> 95295 bytes .readme_assets/trivyk8sNew.jpeg | Bin 0 -> 132884 bytes .readme_assets/vul-misconfig.jpeg | Bin 0 -> 168028 bytes README.md | 31 +++++++++++++++++++++------ docs/CONFIGURATION.md | 3 +++ 8 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 .readme_assets/GitBridgeNew.jpeg create mode 100644 .readme_assets/depricatedAPINew.jpeg create mode 100644 .readme_assets/kubeDataNew.jpeg create mode 100644 .readme_assets/sbom.jpeg create mode 100644 .readme_assets/trivyk8sNew.jpeg create mode 100644 .readme_assets/vul-misconfig.jpeg diff --git a/.readme_assets/GitBridgeNew.jpeg b/.readme_assets/GitBridgeNew.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c851768e8c648a68f435b85cd2bb834d699be4c5 GIT binary patch literal 136730 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 literal 0 HcmV?d00001 diff --git a/.readme_assets/kubeDataNew.jpeg b/.readme_assets/kubeDataNew.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a5c7361f1561ce333da102d1af7a46a82d1e8d7f GIT binary patch literal 140556 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

%JN0)97( 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
0x{BBoO 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 Mg1M*si- literal 0 HcmV?d00001 diff --git a/.readme_assets/trivyk8sNew.jpeg b/.readme_assets/trivyk8sNew.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..81c2abb1d47a81667d099c309b7e198323f0ea9c GIT binary patch literal 132884 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Νn2BTZj52 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 literal 0 HcmV?d00001 diff --git a/.readme_assets/vul-misconfig.jpeg b/.readme_assets/vul-misconfig.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..7e2c7a88ef78aefa9f58fab032cb1db7acbabbb3 GIT binary patch literal 168028 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⪼1{kl5f>^DJa$ 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 ca^V1ObrE*0ObCsKPr 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 Vt1y808`ilt;Yi%?+m^$ze*?aZA-@0s literal 0 HcmV?d00001 diff --git a/README.md b/README.md index b329e4bc..6c484d03 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ ## KubViz -Visualize Kubernetes & DevSecOps Workflows. Tracks changes/events real-time across your entire K8s clusters, git repos, container registries, etc. , analyzing their effects and providing you with the context you need to troubleshoot efficiently. Get the Observability you need, easily. +Visualize Kubernetes & DevSecOps Workflows. Tracks changes/events real-time across your entire K8s clusters, git repos, container registries, Container image Vulnerability scanning, misconfiguration, SBOM etc. , analyzing their effects and providing you with the context you need to troubleshoot efficiently. Get the Observability you need, easily. ## Table of Contents - [How KubViz works](#how-kubviz-works) @@ -50,7 +50,8 @@ KubViz offers a seamless integration with Git repositories, empowering you to ef KubViz also monitors changes in your container registry, providing visibility into image updates. By tracking these changes, KubViz helps you proactively manage container security and compliance. -It comprehensively scans the kubernetes containers for the security flaws such as vulnerabilities and misconfigurations. +It comprehensively scans the kubernetes containers for the security flaws such as vulnerabilities and misconfigurations and creates SBOM. + ## Architecture diagram ![Arch. Diagram](.readme_assets/kubviz.png) @@ -168,6 +169,10 @@ helm upgrade -i kubviz-agent kubviz/agent -n kubviz --set nats.host=" with the IP address of your NATS service **kubviz-client-nats-external**. +**NOTE:** + +A time-based job scheduler is added for each plugins. They allow you to schedule and automate the execution of plugins at specific times, intervals. Each plugin execution can be configured to run at a precise time or at regular intervals. + #### How to Verify if Everything is Up and Running After completing the installation of both the client and agent, you can use the following command to verify if they are up and running. @@ -207,7 +212,7 @@ kubectl --namespace kubviz port-forward $POD_NAME 3000 ### Cluster Event Tracking -Cluster Events +Cluster Events
@@ -221,7 +226,7 @@ Use KubViz to monitor your cluster events, including:
-Deprecated Kubernetes APIs +Deprecated Kubernetes APIs
@@ -235,7 +240,7 @@ Use KubViz to monitor your cluster events, including: ### Git Repository Events Tracking -gitBridge +gitBridge
@@ -261,7 +266,7 @@ Use KubViz to monitor your cluster events, including: ### Kubernetes Container Security Tracking -Kubernetes Container Security Tracking +Kubernetes Container Security Tracking
@@ -270,6 +275,20 @@ Use KubViz to monitor your cluster events, including: - Detects configuration issues in Kubernetes cluster
+Kubernetes Container Security Tracking + +
+ +### SBOM + +sbom + +
+ +- You can generate Software Bill of Materials (SBOM) reports for images within a Kubernetes cluster using KubViz in the CycloneDX format, which will be presented as JSON reports. + +
+
## Contributing diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 8a48ff66..cb89631a 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -34,6 +34,9 @@ Possible values are: Values | Platform | ------ | -------- | `/event/docker/hub` | DockerHub | +`/event/azure/container` | Azure | +`/event/jfrog/container` | JFrog | +`/event/quay/container` | Quay | From 3e5f2f9a71d35b3f489445bd6fe7ec437ce5803d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 26 Sep 2023 10:21:42 +0530 Subject: [PATCH 072/263] fix --- agent/kubviz/trivy.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 29af2f1e..46248c8a 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -4,12 +4,8 @@ import ( "bytes" "encoding/json" "log" - "os" exec "os/exec" - "os/signal" - "runtime/debug" "strings" - "syscall" "github.com/aquasecurity/trivy/pkg/k8s/report" "github.com/google/uuid" @@ -32,30 +28,19 @@ func executeCommandTrivy(command string) ([]byte, error) { return outc.Bytes(), err } -func cleanup() { - log.Println("Performing cleanup...") - // Add your cleanup logic here -} - func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 20m -f json --cache-dir /tmp/.cache --debug" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) - sigCh := make(chan os.Signal, 1) - // Register the signals to handle - signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) // Execute the command out, err := executeCommandTrivy(cmdString) // Handle errors and process the command output as needed if err != nil { log.Printf("Error executing command: %v\n", err) - log.Printf("Command output: %s\n", out) - log.Printf("Stack trace: %v\n", string(debug.Stack())) - } // Log the command output for debugging purposes log.Printf("Command output: %s\n", out) @@ -78,15 +63,6 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { if err != nil { return err } - go func() { - sig := <-sigCh - switch sig { - case syscall.SIGINT, syscall.SIGTERM: - log.Printf("Received termination signal: %v\n", sig) - cleanup() // Perform any necessary cleanup - os.Exit(1) // Exit with a non-zero status code - } - }() return nil } From a5ab558ebd822e5af081d1118d062a544497ded6 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 26 Sep 2023 10:28:04 +0530 Subject: [PATCH 073/263] fix --- agent/kubviz/trivy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 46248c8a..55fab4e0 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -30,7 +30,7 @@ func executeCommandTrivy(command string) ([]byte, error) { } func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport - cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 20m -f json --cache-dir /tmp/.cache --debug" + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir /tmp/.cache --debug" // Log the command before execution log.Printf("Executing command: %s\n", cmdString) From 33206cf14237db8757a4af53ed86f04ef7e65d19 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 28 Sep 2023 12:09:13 +0530 Subject: [PATCH 074/263] review-changes --- .readme_assets/gitcontainerNew.jpeg | Bin 0 -> 146337 bytes README.md | 14 +++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 .readme_assets/gitcontainerNew.jpeg diff --git a/.readme_assets/gitcontainerNew.jpeg b/.readme_assets/gitcontainerNew.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ad3bb14cefd3880e637c7dd29c19f335bc49aea4 GIT binary patch literal 146337 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 Q*hx)i9pHUv{QqwP0Hn%zKmY&$ literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 6c484d03..06b86f35 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ KubViz offers a seamless integration with Git repositories, empowering you to ef KubViz also monitors changes in your container registry, providing visibility into image updates. By tracking these changes, KubViz helps you proactively manage container security and compliance. -It comprehensively scans the kubernetes containers for the security flaws such as vulnerabilities and misconfigurations and creates SBOM. +It comprehensively scans Kubernetes containers for security flaws, such as vulnerabilities and misconfigurations, and creates an SBOM (Software Bill of Materials). ## Architecture diagram @@ -86,6 +86,10 @@ token=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) ```bash helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$token" ``` + +**NOTE:** +- If you want to get a token from a secret, use a secret reference with the secret's name and key. + **NOTE:** - If you want to enable Grafana with the client deployment, add `--set grafana.enabled=true` to the helm upgrade command. @@ -123,6 +127,10 @@ helm upgrade -i kubviz-agent kubviz/agent -n kubviz \ --set container_bridge.enabled=true \ --set "container_bridge.ingress.hosts[0].host=",container_bridge.ingress.hosts[0].paths[0].path=/,container_bridge.ingress.hosts[0].paths[0].pathType=Prefix,container_bridge.ingress.tls[0].secretName=,container_bridge.ingress.tls[0].hosts[0]= ``` + +**NOTE:** +If you want to get a token from a secret, use a secret reference with the secret's name and key. + 3. Replace "INGRESS HOSTNAME" with the desired hostname for the Git Bridge and Container Bridge Ingress configurations. 4. Replace "SECRET-NAME" with the desired secretname for the Git Bridge and Container Bridge Ingress configurations. @@ -254,7 +262,7 @@ Use KubViz to monitor your cluster events, including: ### Container Registry Events Tracking -Container Registry Events Tracking +Container Registry Events Tracking
@@ -285,7 +293,7 @@ Use KubViz to monitor your cluster events, including:
-- You can generate Software Bill of Materials (SBOM) reports for images within a Kubernetes cluster using KubViz in the CycloneDX format, which will be presented as JSON reports. +- Generate reports for Software Bill of Materials (SBOM) from images within your Kubernetes cluster using KubViz in the CycloneDX format. These reports will be available in JSON format.
From 91b3a6b625afbc8925af0ee9d6c8b68eb35d1d54 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 29 Sep 2023 11:57:41 +0530 Subject: [PATCH 075/263] default values --- agent/config/config.go | 4 +-- agent/kubviz/k8smetrics_agent.go | 53 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/agent/config/config.go b/agent/config/config.go index e97b3e11..73fddddf 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -8,13 +8,13 @@ import ( type AgentConfigurations struct { SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` SAName string `envconfig:"SA_NAME" default:"default"` - OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"@every 20m"` + OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"0"` GetAllInterval string `envconfig:"GETALL_INTERVAL" default:"*/30 * * * *"` KubeScoreInterval string `envconfig:"KUBESCORE_INTERVAL" default:"*/40 * * * *"` RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` TrivyInterval string `envconfig:"TRIVY_INTERVAL" default:"*/10 * * * *"` - SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"false"` + SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"true"` } func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 8cef0799..b6fd68c9 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -64,18 +64,19 @@ var ( ) func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { - err := RunTrivyK8sClusterScan(js) + err := RunTrivyImageScans(config, js) if err != nil { return err } - // err = RunTrivyImageScans(config, js) - // if err != nil { - // return err - // } - // err = RunTrivySbomScan(config, js) - // if err != nil { - // return err - // } + err = RunTrivySbomScan(config, js) + if err != nil { + return err + } + err = RunTrivyK8sClusterScan(js) + if err != nil { + return err + } + return nil } @@ -118,25 +119,23 @@ func main() { go publishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { - // err := outDatedImages(config, js) - // LogErr(err) - // err = KubePreUpgradeDetector(config, js) - // LogErr(err) - // err = GetAllResources(config, js) - // LogErr(err) - // err = RakeesOutput(config, js) - // LogErr(err) + err := outDatedImages(config, js) + LogErr(err) + err = KubePreUpgradeDetector(config, js) + LogErr(err) + err = GetAllResources(config, js) + LogErr(err) + err = RakeesOutput(config, js) + LogErr(err) // getK8sEvents(clientset) - err := runTrivyScans(config, js) + err = runTrivyScans(config, js) LogErr(err) // err = RunKubeScore(clientset, js) // LogErr(err) } collectAndPublishMetrics() - if schedulingIntervalStr == "" { - schedulingIntervalStr = "20m" - } + if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := initScheduler(config, js, *cfg, clientset) @@ -301,7 +300,7 @@ func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.AgentConfigurations, clientset *kubernetes.Clientset) (s *Scheduler) { log := logging.NewLogger() s = NewScheduler(log) - if cfg.OutdatedInterval != "" { + if cfg.OutdatedInterval != "" && cfg.OutdatedInterval != "0" { sj, err := NewOutDatedImagesJob(config, js, cfg.OutdatedInterval) if err != nil { log.Fatal("no time interval", err) @@ -311,7 +310,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age log.Fatal("failed to do job", err) } } - if cfg.GetAllInterval != "" { + if cfg.GetAllInterval != "" && cfg.GetAllInterval != "0" { sj, err := NewKetallJob(config, js, cfg.GetAllInterval) if err != nil { log.Fatal("no time interval", err) @@ -321,7 +320,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age log.Fatal("failed to do job", err) } } - if cfg.KubeScoreInterval != "" { + if cfg.KubeScoreInterval != "" && cfg.KubeScoreInterval != "0" { sj, err := NewKubescoreJob(clientset, js, cfg.KubeScoreInterval) if err != nil { log.Fatal("no time interval", err) @@ -331,7 +330,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age log.Fatal("failed to do job", err) } } - if cfg.RakkessInterval != "" { + if cfg.RakkessInterval != "" && cfg.RakkessInterval != "0" { sj, err := NewRakkessJob(config, js, cfg.RakkessInterval) if err != nil { log.Fatal("no time interval", err) @@ -341,7 +340,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age log.Fatal("failed to do job", err) } } - if cfg.KubePreUpgradeInterval != "" { + if cfg.KubePreUpgradeInterval != "" && cfg.KubePreUpgradeInterval != "0" { sj, err := NewKubePreUpgradeJob(config, js, cfg.KubePreUpgradeInterval) if err != nil { log.Fatal("no time interval", err) @@ -351,7 +350,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age log.Fatal("failed to do job", err) } } - if cfg.TrivyInterval != "" { + if cfg.TrivyInterval != "" && cfg.TrivyInterval != "0" { sj, err := NewTrivyJob(config, js, cfg.TrivyInterval) if err != nil { log.Fatal("no time interval", err) From 0a99d0863c5525081db3310366b6bc5903960a76 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 29 Sep 2023 14:36:18 +0530 Subject: [PATCH 076/263] default values --- agent/kubviz/scheduler_watch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler_watch.go index 90de3d25..6683816d 100644 --- a/agent/kubviz/scheduler_watch.go +++ b/agent/kubviz/scheduler_watch.go @@ -84,7 +84,7 @@ func (v *KubePreUpgradeJob) CronSpec() string { func (j *KubePreUpgradeJob) Run() { // Call the Kubepreupgrade function with the provided config and js - err := GetAllResources(j.config, j.js) + err := KubePreUpgradeDetector(j.config, j.js) LogErr(err) } From 0ce9e600aa39d7143354e4da2a966a4dd45f2826 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 29 Sep 2023 14:41:50 +0530 Subject: [PATCH 077/263] default values --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index b6fd68c9..53928b5c 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -130,8 +130,8 @@ func main() { // getK8sEvents(clientset) err = runTrivyScans(config, js) LogErr(err) - // err = RunKubeScore(clientset, js) - // LogErr(err) + err = RunKubeScore(clientset, js) + LogErr(err) } collectAndPublishMetrics() From d425be9962e0e240cb96217fbcd8f6f0c6cf70f9 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 29 Sep 2023 14:42:53 +0530 Subject: [PATCH 078/263] default values --- model/trivy.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/model/trivy.go b/model/trivy.go index f031c5c3..f7d564da 100644 --- a/model/trivy.go +++ b/model/trivy.go @@ -3,9 +3,7 @@ package model import "github.com/aquasecurity/trivy/pkg/k8s/report" type Trivy struct { - ID string - ClusterName string - Report report.ConsolidatedReport - CompressedReport []byte - UncompressedReport []byte + ID string + ClusterName string + Report report.ConsolidatedReport } From 49b02ac912342cc80c8d943f88671ba1de721387 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 29 Sep 2023 14:47:57 +0530 Subject: [PATCH 079/263] default values --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 53928b5c..b6fd68c9 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -130,8 +130,8 @@ func main() { // getK8sEvents(clientset) err = runTrivyScans(config, js) LogErr(err) - err = RunKubeScore(clientset, js) - LogErr(err) + // err = RunKubeScore(clientset, js) + // LogErr(err) } collectAndPublishMetrics() From e7844144dee17fd7f7aa1dfafa77db64037c1555 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 29 Sep 2023 15:35:34 +0530 Subject: [PATCH 080/263] Fix --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index b6fd68c9..53928b5c 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -130,8 +130,8 @@ func main() { // getK8sEvents(clientset) err = runTrivyScans(config, js) LogErr(err) - // err = RunKubeScore(clientset, js) - // LogErr(err) + err = RunKubeScore(clientset, js) + LogErr(err) } collectAndPublishMetrics() From 70afe9cf77ed01352e1cf8a194c0b107248e131c Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Fri, 6 Oct 2023 13:37:41 +0530 Subject: [PATCH 081/263] Fixed dashboard --- grafana/gitLab-dashboard.json | 2 +- grafana/kubvizDsahboard.json | 189 +++++----------------------------- grafana/trivy-dashboard.json | 25 ++++- 3 files changed, 50 insertions(+), 166 deletions(-) diff --git a/grafana/gitLab-dashboard.json b/grafana/gitLab-dashboard.json index 962b582e..feb82973 100644 --- a/grafana/gitLab-dashboard.json +++ b/grafana/gitLab-dashboard.json @@ -193,7 +193,7 @@ "skip_comments": true } ], - "title": "Number of GitHub Merge events grouped by author", + "title": "Number of GitLab Merge events grouped by author", "type": "volkovlabs-echarts-panel" }, { diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index b12ae28e..e4968f20 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 8, + "id": 126, "links": [], "liveNow": false, "panels": [ @@ -114,13 +114,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -190,13 +184,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Outdated Images", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -266,13 +254,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -342,13 +324,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "DeletedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -418,13 +394,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "DeprecatedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -494,13 +464,7 @@ "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -776,13 +740,7 @@ "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -851,13 +809,7 @@ "description": "This panel displays the total number of pods with Created state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -928,13 +880,7 @@ "description": "This panel displays the total number of pods with backOff state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1005,13 +951,7 @@ "description": "This panel displays the total number of pods with Unhealthy state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1086,13 +1026,7 @@ "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1161,13 +1095,7 @@ "description": "This panel displays the total number of nodes which is in not ready state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1238,13 +1166,7 @@ "description": "This panel displays the total number of nodes which is in ready state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1315,13 +1237,7 @@ "description": "This panel displays the total number of nodes which is in NodeHasNoDiskPressure state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1403,13 +1319,7 @@ "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1730,13 +1640,7 @@ "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1845,13 +1749,7 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "DeprecatedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1951,13 +1849,7 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "DeletedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2057,13 +1949,7 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Outdated Images", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2162,13 +2048,7 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2268,13 +2148,7 @@ "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2360,13 +2234,7 @@ "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2588,8 +2456,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2693,8 +2560,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2798,8 +2664,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2881,6 +2746,6 @@ "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 2, + "version": 1, "weekStart": "" } \ No newline at end of file diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 917c5b1c..21017e18 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 86, + "id": 125, "links": [], "liveNow": false, "panels": [ @@ -155,6 +155,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -225,6 +226,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -292,6 +294,7 @@ "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "percentage", "steps": [ @@ -360,6 +363,7 @@ "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "percentage", "steps": [ @@ -431,6 +435,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -501,6 +506,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -571,6 +577,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -641,6 +648,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -711,6 +719,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -781,6 +790,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -851,6 +861,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -921,6 +932,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -992,6 +1004,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1062,6 +1075,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1139,6 +1153,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1211,6 +1226,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1283,6 +1299,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1355,11 +1372,13 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -1423,6 +1442,6 @@ "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 3, + "version": 1, "weekStart": "" } \ No newline at end of file From e32c9c3832f5c90044447f8765a9be8c58177c64 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Fri, 6 Oct 2023 22:14:33 +0530 Subject: [PATCH 082/263] Updated gitlab,kubviz,trivy dashboards --- charts/client/Chart.yaml | 2 +- .../templates/configmap-gitlab-dashboard.yaml | 2 +- .../templates/configmap-kubviz-dashboard.yaml | 189 +++--------------- .../templates/configmap-trivy-dashboard.yaml | 25 ++- 4 files changed, 51 insertions(+), 167 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 7d410ec0..3d467ac8 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.4 +version: 1.1.5 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-gitlab-dashboard.yaml b/charts/client/templates/configmap-gitlab-dashboard.yaml index e9b69191..5761e68f 100644 --- a/charts/client/templates/configmap-gitlab-dashboard.yaml +++ b/charts/client/templates/configmap-gitlab-dashboard.yaml @@ -204,7 +204,7 @@ data: "skip_comments": true } ], - "title": "Number of GitHub Merge events grouped by author", + "title": "Number of GitLab Merge events grouped by author", "type": "volkovlabs-echarts-panel" }, { diff --git a/charts/client/templates/configmap-kubviz-dashboard.yaml b/charts/client/templates/configmap-kubviz-dashboard.yaml index eec05960..795593ea 100644 --- a/charts/client/templates/configmap-kubviz-dashboard.yaml +++ b/charts/client/templates/configmap-kubviz-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 8, + "id": 126, "links": [], "liveNow": false, "panels": [ @@ -125,13 +125,7 @@ data: "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -201,13 +195,7 @@ data: "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Outdated Images", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -277,13 +265,7 @@ data: "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -353,13 +335,7 @@ data: "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "DeletedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -429,13 +405,7 @@ data: "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "DeprecatedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -505,13 +475,7 @@ data: "color": { "mode": "thresholds" }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -787,13 +751,7 @@ data: "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -862,13 +820,7 @@ data: "description": "This panel displays the total number of pods with Created state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -939,13 +891,7 @@ data: "description": "This panel displays the total number of pods with backOff state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1016,13 +962,7 @@ data: "description": "This panel displays the total number of pods with Unhealthy state.", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1097,13 +1037,7 @@ data: "fixedColor": "#249b6a", "mode": "fixed" }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1172,13 +1106,7 @@ data: "description": "This panel displays the total number of nodes which is in not ready state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1249,13 +1177,7 @@ data: "description": "This panel displays the total number of nodes which is in ready state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1326,13 +1248,7 @@ data: "description": "This panel displays the total number of nodes which is in NodeHasNoDiskPressure state", "fieldConfig": { "defaults": { - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "percentage", @@ -1414,13 +1330,7 @@ data: "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1741,13 +1651,7 @@ data: "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "KubeData", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1690551079516&to=1690551979516" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1856,13 +1760,7 @@ data: "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "DeprecatedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -1962,13 +1860,7 @@ data: "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "DeletedAPIs", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2068,13 +1960,7 @@ data: "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Outdated Images", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2173,13 +2059,7 @@ data: "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Kubedata", - "url": "https://grafana.alpha.optimizor.app/d/Qq-FK1rVz/kubedata?orgId=1&from=1689917173495&to=1689918073495" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2279,13 +2159,7 @@ data: "mode": "off" } }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2371,13 +2245,7 @@ data: "filterable": true, "inspect": false }, - "links": [ - { - "targetBlank": true, - "title": "Kubernetes Resources", - "url": "https://grafana.alpha.optimizor.app/d/o2M7hbrVk/kubviz-features?orgId=1&from=1689896094681&to=1689917694681" - } - ], + "links": [], "mappings": [], "thresholds": { "mode": "absolute", @@ -2599,8 +2467,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2704,8 +2571,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2809,8 +2675,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2892,7 +2757,7 @@ data: "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 434329d9..314e613b 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 86, + "id": 125, "links": [], "liveNow": false, "panels": [ @@ -166,6 +166,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -236,6 +237,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -303,6 +305,7 @@ data: "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "percentage", "steps": [ @@ -371,6 +374,7 @@ data: "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "percentage", "steps": [ @@ -442,6 +446,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -512,6 +517,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -582,6 +588,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -652,6 +659,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -722,6 +730,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -792,6 +801,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -862,6 +872,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -932,6 +943,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1003,6 +1015,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1073,6 +1086,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1150,6 +1164,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1222,6 +1237,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1294,6 +1310,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1366,11 +1383,13 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -1434,7 +1453,7 @@ data: "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 3, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file From 4f1ae495a41f9104b8e6ad7018078b04e0562942 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 10 Oct 2023 21:27:50 +0530 Subject: [PATCH 083/263] added gitbridge and containerbridge folders in dashboards --- charts/client/Chart.yaml | 2 +- charts/client/templates/configmap-azure-dashboard.yaml | 2 +- charts/client/templates/configmap-bitbucket-dashboard.yaml | 2 +- charts/client/templates/configmap-clickhouse-datasource.yaml | 2 +- .../client/templates/configmap-containerbridge-dashboard.yaml | 2 +- charts/client/templates/configmap-gitea-dashboard.yaml | 2 +- charts/client/templates/configmap-github-dashboard.yaml | 2 +- charts/client/templates/configmap-gitlab-dashboard.yaml | 2 +- charts/client/templates/configmap-kubviz-dashboard.yaml | 2 +- charts/client/templates/configmap-vertamedia-datasource.yaml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 3d467ac8..0a00498a 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.5 +version: 1.1.6 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-azure-dashboard.yaml b/charts/client/templates/configmap-azure-dashboard.yaml index 8025b20a..f0bfca39 100644 --- a/charts/client/templates/configmap-azure-dashboard.yaml +++ b/charts/client/templates/configmap-azure-dashboard.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-azure-dashboard annotations: - grafana_folder: "Kubviz" + grafana_folder: "Gitbridge" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: diff --git a/charts/client/templates/configmap-bitbucket-dashboard.yaml b/charts/client/templates/configmap-bitbucket-dashboard.yaml index caa52a35..04a61d88 100644 --- a/charts/client/templates/configmap-bitbucket-dashboard.yaml +++ b/charts/client/templates/configmap-bitbucket-dashboard.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-bitbucket-dashboard annotations: - grafana_folder: "Kubviz" + grafana_folder: "Gitbridge" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: diff --git a/charts/client/templates/configmap-clickhouse-datasource.yaml b/charts/client/templates/configmap-clickhouse-datasource.yaml index fac0f087..b28461cd 100644 --- a/charts/client/templates/configmap-clickhouse-datasource.yaml +++ b/charts/client/templates/configmap-clickhouse-datasource.yaml @@ -13,6 +13,6 @@ data: type: grafana-clickhouse-datasource jsonData: port: 9000 - server: kubviz-client-clickhouse + server: {{ include "client.fullname" . }}-clickhouse tlsSkipVerify: true {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-containerbridge-dashboard.yaml b/charts/client/templates/configmap-containerbridge-dashboard.yaml index 48a72bd8..fa45e2e5 100644 --- a/charts/client/templates/configmap-containerbridge-dashboard.yaml +++ b/charts/client/templates/configmap-containerbridge-dashboard.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-containerbridge-dashboard annotations: - grafana_folder: "Kubviz" + grafana_folder: "Containerbridge" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: diff --git a/charts/client/templates/configmap-gitea-dashboard.yaml b/charts/client/templates/configmap-gitea-dashboard.yaml index 43782f56..db0eca73 100644 --- a/charts/client/templates/configmap-gitea-dashboard.yaml +++ b/charts/client/templates/configmap-gitea-dashboard.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-gitea-dashboard annotations: - grafana_folder: "Kubviz" + grafana_folder: "Gitbridge" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: diff --git a/charts/client/templates/configmap-github-dashboard.yaml b/charts/client/templates/configmap-github-dashboard.yaml index 12a5029d..9373cd00 100644 --- a/charts/client/templates/configmap-github-dashboard.yaml +++ b/charts/client/templates/configmap-github-dashboard.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-github-dashboard annotations: - grafana_folder: "Kubviz" + grafana_folder: "Gitbridge" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: diff --git a/charts/client/templates/configmap-gitlab-dashboard.yaml b/charts/client/templates/configmap-gitlab-dashboard.yaml index 5761e68f..382d3e04 100644 --- a/charts/client/templates/configmap-gitlab-dashboard.yaml +++ b/charts/client/templates/configmap-gitlab-dashboard.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ include "client.fullname" . }}-gitlab-dashboard annotations: - grafana_folder: "Kubviz" + grafana_folder: "Gitbridge" labels: {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} data: diff --git a/charts/client/templates/configmap-kubviz-dashboard.yaml b/charts/client/templates/configmap-kubviz-dashboard.yaml index 795593ea..c79ea4bb 100644 --- a/charts/client/templates/configmap-kubviz-dashboard.yaml +++ b/charts/client/templates/configmap-kubviz-dashboard.yaml @@ -2750,7 +2750,7 @@ data: "list": [] }, "time": { - "from": "now-5m", + "from": "now-24h", "to": "now" }, "timepicker": {}, diff --git a/charts/client/templates/configmap-vertamedia-datasource.yaml b/charts/client/templates/configmap-vertamedia-datasource.yaml index b2ea28fe..3b2ce491 100644 --- a/charts/client/templates/configmap-vertamedia-datasource.yaml +++ b/charts/client/templates/configmap-vertamedia-datasource.yaml @@ -11,6 +11,6 @@ data: datasources: - name: vertamedia-clickhouse-datasource type: vertamedia-clickhouse-datasource - url: http://kubviz-client-clickhouse:8123 + url: {{ include "client.fullname" . }}-clickhouse:8123 access: proxy {{- end }} \ No newline at end of file From 84d27c6b788297a4c400a459a8e33fb9d57ef42b Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 11 Oct 2023 19:15:34 +0530 Subject: [PATCH 084/263] scheduler-readme-updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06b86f35..0912d993 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ helm upgrade -i kubviz-agent kubviz/agent -n kubviz --set nats.host= Date: Thu, 12 Oct 2023 11:46:26 +0530 Subject: [PATCH 085/263] updated client and agent chart version --- charts/agent/Chart.yaml | 4 ++-- charts/agent/values.yaml | 6 +++--- charts/client/Chart.yaml | 2 +- charts/client/values.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index e85bbae2..affcc4b4 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.3 +version: 1.1.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.0.0" +appVersion: "v1.1.3" diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 0787dc3a..f30e876b 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/kubviz-agent pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.0" + tag: "v1.1.3" imagePullSecrets: [] nameOverride: "" @@ -47,7 +47,7 @@ git_bridge: image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always - tag: "v1.1.0" + tag: "v1.1.3" resources: limits: cpu: 200m @@ -81,7 +81,7 @@ container_bridge: image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always - tag: "v1.1.0" + tag: "v1.1.3" resources: limits: cpu: 200m diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 0a00498a..54675f1b 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.6 +version: 1.1.7 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 32f4fde7..3b4f7fde 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/client pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.0" + tag: "v1.1.3" imagePullSecrets: [] nameOverride: "" @@ -121,6 +121,6 @@ migration: image: repository: ghcr.io/intelops/kubviz/migration pullPolicy: Always - tag: "v1.1.0" + tag: "v1.1.3" schema: path: "/sql" From a0bda3b698da837c3bcb3310660fd25b8f52d5b5 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sat, 14 Oct 2023 20:22:23 +0530 Subject: [PATCH 086/263] updated agent chart --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index affcc4b4..4ef1b0e0 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.4 +version: 1.1.5 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index f30e876b..2643bf52 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -159,6 +159,6 @@ nats: # Use token if you want to provide the token via Helm Values token: "" # Use a secret reference if you want to get a token from a secret - secret: - name: "" - key: "" + # secret: + # name: "" + # key: "" From 731514bcb18bbbc30878b2f66af323039d4a2bea Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 23 Oct 2023 15:22:48 +0530 Subject: [PATCH 087/263] added auth in clickhouse --- client/pkg/clickhouse/db_client.go | 45 ++++++++++++++++++++++-------- client/pkg/config/config.go | 10 ++++--- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index e14ed053..3b2accf9 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -50,17 +50,40 @@ type DBInterface interface { func NewDBClient(conf *config.Config) (DBInterface, error) { ctx := context.Background() - log.Println("Connecting to Clickhouse DB and creating schemas...") - splconn, err := clickhouse.Open(&clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, - Debug: true, - Debugf: func(format string, v ...any) { - fmt.Printf(format, v) - }, - Settings: clickhouse.Settings{ - "allow_experimental_object_type": 1, - }, - }) + var connOptions clickhouse.Options + + if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { + connOptions = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: conf.ClickHouseUsername, + Password: conf.ClickHousePassword, + }, + Debugf: func(format string, v ...interface{}) { + fmt.Printf(format, v...) + }, + Settings: clickhouse.Settings{ + "allow_experimental_object_type": 1, + }, + } + fmt.Printf("Connecting to ClickHouse using usename and password") + } else { + connOptions = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Debug: true, + Debugf: func(format string, v ...interface{}) { + fmt.Printf(format, v...) + }, + Settings: clickhouse.Settings{ + "allow_experimental_object_type": 1, + }, + } + fmt.Printf("Connecting to ClickHouse without usename and password") + + } + + splconn, err := clickhouse.Open(&connOptions) if err != nil { return nil, err } diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index b50c970e..3ebb9cf1 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -1,8 +1,10 @@ package config type Config struct { - NatsAddress string `envconfig:"NATS_ADDRESS"` - NatsToken string `envconfig:"NATS_TOKEN"` - DbPort int `envconfig:"DB_PORT"` - DBAddress string `envconfig:"DB_ADDRESS"` + NatsAddress string `envconfig:"NATS_ADDRESS"` + NatsToken string `envconfig:"NATS_TOKEN"` + DbPort int `envconfig:"DB_PORT"` + DBAddress string `envconfig:"DB_ADDRESS"` + ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` + ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` } From 1790bd0720cade64ab71101dc672b23152648750 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Wed, 25 Oct 2023 10:54:52 +0530 Subject: [PATCH 088/263] added comment --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 3b2accf9..692af957 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -67,7 +67,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { "allow_experimental_object_type": 1, }, } - fmt.Printf("Connecting to ClickHouse using usename and password") + fmt.Printf("Connecting to ClickHouse using username and password") } else { connOptions = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, From 5cf6c43a4e281dfdb28b939564640edcff11df2f Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 12:27:05 +0530 Subject: [PATCH 089/263] changes in port --- agent/kubviz/k8smetrics_agent.go | 2 +- client/pkg/clickhouse/db_client.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 53928b5c..49230271 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -127,7 +127,7 @@ func main() { LogErr(err) err = RakeesOutput(config, js) LogErr(err) - // getK8sEvents(clientset) + //getK8sEvents(clientset) err = runTrivyScans(config, js) LogErr(err) err = RunKubeScore(clientset, js) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 692af957..2d17f614 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -54,7 +54,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { connOptions = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Addr: []string{fmt.Sprintf("%s:%d?%s&%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, Debug: true, Auth: clickhouse.Auth{ Username: conf.ClickHouseUsername, @@ -92,7 +92,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if exception, ok := err.(*clickhouse.Exception); ok { fmt.Printf("[%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace) } else { - fmt.Println(err) + fmt.Println("Authentication error:", err) // Print the error message here } return nil, err } @@ -110,7 +110,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if exception, ok := err.(*clickhouse.Exception); ok { fmt.Printf("[%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace) } else { - fmt.Println(err) + fmt.Println("Authenticate error:", err) } return nil, err } From 72f492eef5237d0fd9406bd6fcd7a41729836d07 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 12:43:57 +0530 Subject: [PATCH 090/263] changes in port --- client/pkg/clickhouse/db_client.go | 62 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 2d17f614..93c5a07a 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -50,38 +50,38 @@ type DBInterface interface { func NewDBClient(conf *config.Config) (DBInterface, error) { ctx := context.Background() - var connOptions clickhouse.Options - - if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { - connOptions = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?%s&%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: conf.ClickHouseUsername, - Password: conf.ClickHousePassword, - }, - Debugf: func(format string, v ...interface{}) { - fmt.Printf(format, v...) - }, - Settings: clickhouse.Settings{ - "allow_experimental_object_type": 1, - }, - } - fmt.Printf("Connecting to ClickHouse using username and password") - } else { - connOptions = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, - Debug: true, - Debugf: func(format string, v ...interface{}) { - fmt.Printf(format, v...) - }, - Settings: clickhouse.Settings{ - "allow_experimental_object_type": 1, - }, - } - fmt.Printf("Connecting to ClickHouse without usename and password") + // var connOptions clickhouse.Options + + // if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { + connOptions := clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: "admin", + Password: "admin", + }, + Debugf: func(format string, v ...interface{}) { + fmt.Printf(format, v...) + }, + Settings: clickhouse.Settings{ + "allow_experimental_object_type": 1, + }, + } + fmt.Printf("Connecting to ClickHouse using username and password") + // } else { + // connOptions = clickhouse.Options{ + // Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + // Debug: true, + // Debugf: func(format string, v ...interface{}) { + // fmt.Printf(format, v...) + // }, + // Settings: clickhouse.Settings{ + // "allow_experimental_object_type": 1, + // }, + // } + // fmt.Printf("Connecting to ClickHouse without usename and password") - } + // } splconn, err := clickhouse.Open(&connOptions) if err != nil { From ebfbbb5fbefa7f6b5337d8097dcefef09c023c0a Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 13:15:19 +0530 Subject: [PATCH 091/263] changes in port --- client/pkg/clickhouse/db_client.go | 62 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 93c5a07a..81de72f5 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -50,38 +50,38 @@ type DBInterface interface { func NewDBClient(conf *config.Config) (DBInterface, error) { ctx := context.Background() - // var connOptions clickhouse.Options - - // if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { - connOptions := clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: "admin", - Password: "admin", - }, - Debugf: func(format string, v ...interface{}) { - fmt.Printf(format, v...) - }, - Settings: clickhouse.Settings{ - "allow_experimental_object_type": 1, - }, - } - fmt.Printf("Connecting to ClickHouse using username and password") - // } else { - // connOptions = clickhouse.Options{ - // Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, - // Debug: true, - // Debugf: func(format string, v ...interface{}) { - // fmt.Printf(format, v...) - // }, - // Settings: clickhouse.Settings{ - // "allow_experimental_object_type": 1, - // }, - // } - // fmt.Printf("Connecting to ClickHouse without usename and password") + var connOptions clickhouse.Options + + if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { + connOptions = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: conf.ClickHouseUsername, + Password: conf.ClickHousePassword, + }, + Debugf: func(format string, v ...interface{}) { + fmt.Printf(format, v...) + }, + Settings: clickhouse.Settings{ + "allow_experimental_object_type": 1, + }, + } + fmt.Printf("Connecting to ClickHouse using username and password") + } else { + connOptions = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Debug: true, + Debugf: func(format string, v ...interface{}) { + fmt.Printf(format, v...) + }, + Settings: clickhouse.Settings{ + "allow_experimental_object_type": 1, + }, + } + fmt.Printf("Connecting to ClickHouse without usename and password") - // } + } splconn, err := clickhouse.Open(&connOptions) if err != nil { From 04ffb7782addc6845acce844062d28bb81723996 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 13:17:15 +0530 Subject: [PATCH 092/263] changes in port --- client/pkg/clickhouse/db_client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 81de72f5..22b8a518 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -53,6 +53,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { var connOptions clickhouse.Options if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { + fmt.Println("Using provided username and password") connOptions = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, Debug: true, From 0146a7075392d55933dedd71c85d7515a7811ac7 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 13:24:14 +0530 Subject: [PATCH 093/263] changes in port --- client/pkg/clickhouse/db_client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 22b8a518..c94f387c 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -70,6 +70,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { } fmt.Printf("Connecting to ClickHouse using username and password") } else { + fmt.Println("Using connection without username and password") connOptions = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, Debug: true, From cf21e57afbce807f6512d6b7e147aa8d3325d0b1 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 13:34:55 +0530 Subject: [PATCH 094/263] changes in port --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index c94f387c..1eca5223 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -55,7 +55,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { fmt.Println("Using provided username and password") connOptions = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, Debug: true, Auth: clickhouse.Auth{ Username: conf.ClickHouseUsername, From fbd91ceab458259d46f6b16087c9f129bfbff5ea Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 14:47:27 +0530 Subject: [PATCH 095/263] changes in port --- client/pkg/clickhouse/db_client.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 1eca5223..4ea9b60b 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -106,8 +106,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { // } // } stdconn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, - }) + Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}}) if err := stdconn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { fmt.Printf("[%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace) From 6243c98d1018506cae2bfbc3535f63871a54c4ee Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 15:01:31 +0530 Subject: [PATCH 096/263] changes in address --- client/pkg/clickhouse/db_client.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 4ea9b60b..6fa3909b 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -106,7 +106,13 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { // } // } stdconn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}}) + Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: conf.ClickHouseUsername, + Password: conf.ClickHousePassword, + }, + }) if err := stdconn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { fmt.Printf("[%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace) From d732043f0423d153174e6231ffcafbdef28125c2 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 15:12:42 +0530 Subject: [PATCH 097/263] changes in address --- client/pkg/clickhouse/db_client.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 6fa3909b..8581225e 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -57,10 +57,6 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { connOptions = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, Debug: true, - Auth: clickhouse.Auth{ - Username: conf.ClickHouseUsername, - Password: conf.ClickHousePassword, - }, Debugf: func(format string, v ...interface{}) { fmt.Printf(format, v...) }, From 242aeea7efe09d680c9191ce61aff648aa620a99 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 16:54:12 +0530 Subject: [PATCH 098/263] changes in address --- client/pkg/clickhouse/db_client.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 8581225e..1f6c6842 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -57,6 +57,10 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { connOptions = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, Debug: true, + Auth: clickhouse.Auth{ + Username: conf.ClickHouseUsername, + Password: conf.ClickHousePassword, + }, Debugf: func(format string, v ...interface{}) { fmt.Printf(format, v...) }, @@ -101,22 +105,29 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { // return nil, err // } // } - stdconn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: conf.ClickHouseUsername, - Password: conf.ClickHousePassword, - }, - }) + if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { + fmt.Println("Using provided username and password") + connOptions = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, + } + } else { + fmt.Println("Using connection without username and password") + connOptions = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + } + } + + stdconn := clickhouse.OpenDB(&connOptions) + if err := stdconn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { fmt.Printf("[%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace) } else { - fmt.Println("Authenticate error:", err) + fmt.Println("Authentication error:", err) } return nil, err } + return &DBClient{splconn: splconn, conn: stdconn, conf: conf}, nil } From 7c45bec1aa0c29fd08ca4aa759e74a1bc604de57 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 16:58:11 +0530 Subject: [PATCH 099/263] changes in address --- client/pkg/clickhouse/db_client.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 1f6c6842..424d75f7 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -105,19 +105,21 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { // return nil, err // } // } + var connOption clickhouse.Options + if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { fmt.Println("Using provided username and password") - connOptions = clickhouse.Options{ + connOption = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, } } else { fmt.Println("Using connection without username and password") - connOptions = clickhouse.Options{ + connOption = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, } } - stdconn := clickhouse.OpenDB(&connOptions) + stdconn := clickhouse.OpenDB(&connOption) if err := stdconn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { From 2d01374e4ec1d909146941fe5afba05cc7593623 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 20:22:31 +0530 Subject: [PATCH 100/263] changes in migeration --- cmd/cli/config/config.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 394097c2..11ce4c9d 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -9,9 +9,11 @@ import ( ) type Config struct { - DbPort int `envconfig:"DB_PORT" required:"true"` - DBAddress string `envconfig:"DB_ADDRESS" required:"true"` - SchemaPath string `envconfig:"SCHEMA_PATH" default:"/sql"` + DbPort int `envconfig:"DB_PORT" required:"true"` + DBAddress string `envconfig:"DB_ADDRESS" required:"true"` + ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` + ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` + SchemaPath string `envconfig:"SCHEMA_PATH" default:"/sql"` } func OpenClickHouseConn() (*sql.DB, *Config, error) { @@ -21,7 +23,7 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { return nil, nil, err } conn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", cfg.DBAddress, cfg.DbPort, cfg.ClickHouseUsername, cfg.ClickHousePassword)}, }) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { From a5d62999af34179d2e8df5cc5585045fa56e8159 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 21:02:53 +0530 Subject: [PATCH 101/263] changes in migeration --- cmd/cli/config/config.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 11ce4c9d..ceeaeda5 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -23,7 +23,8 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { return nil, nil, err } conn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", cfg.DBAddress, cfg.DbPort, cfg.ClickHouseUsername, cfg.ClickHousePassword)}, + Addr: []string{ + fmt.Sprintf("%s:%d?username=%s&password=%s", cfg.DBAddress, cfg.DbPort, cfg.ClickHouseUsername, cfg.ClickHousePassword)}, }) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { From 9d345bd7029cadb17b7004f70481b282ffa7b518 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 21:20:00 +0530 Subject: [PATCH 102/263] changes in migeration --- cmd/cli/config/config.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index ceeaeda5..61f1e615 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -24,7 +24,12 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { } conn := clickhouse.OpenDB(&clickhouse.Options{ Addr: []string{ - fmt.Sprintf("%s:%d?username=%s&password=%s", cfg.DBAddress, cfg.DbPort, cfg.ClickHouseUsername, cfg.ClickHousePassword)}, + fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: cfg.ClickHouseUsername, + Password: cfg.ClickHousePassword, + }, }) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { From baf85cc2636cf0de402b7cea9ee9447224cb6b09 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 21:29:23 +0530 Subject: [PATCH 103/263] changes in migeration --- client/pkg/clickhouse/db_client.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 424d75f7..3b9cfa1b 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -55,7 +55,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { fmt.Println("Using provided username and password") connOptions = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, Debug: true, Auth: clickhouse.Auth{ Username: conf.ClickHouseUsername, @@ -110,7 +110,12 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { fmt.Println("Using provided username and password") connOption = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, + Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: conf.ClickHouseUsername, + Password: conf.ClickHousePassword, + }, } } else { fmt.Println("Using connection without username and password") From 630e057962d692150f4a158964357639c7c8328a Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 27 Oct 2023 21:36:01 +0530 Subject: [PATCH 104/263] changes in migeration --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 3b9cfa1b..cd910e3b 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -110,7 +110,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { fmt.Println("Using provided username and password") connOption = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d?username=%s&password=%s", conf.DBAddress, conf.DbPort, conf.ClickHouseUsername, conf.ClickHousePassword)}, + Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, Debug: true, Auth: clickhouse.Auth{ Username: conf.ClickHouseUsername, From 8e371964bbf67688012b1e4b9ad272db5d7b7395 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 30 Oct 2023 05:47:28 +0530 Subject: [PATCH 105/263] fix --- cmd/cli/config/config.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 61f1e615..61a7cfb2 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -22,15 +22,26 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { if err != nil { return nil, nil, err } - conn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{ - fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: cfg.ClickHouseUsername, - Password: cfg.ClickHousePassword, - }, - }) + var conns clickhouse.Options + + if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { + fmt.Println("Using provided username and password") + conns = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: cfg.ClickHouseUsername, + Password: cfg.ClickHousePassword, + }, + } + } else { + fmt.Println("Using connection without username and password") + conns = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + } + } + + conn := clickhouse.OpenDB(&conns) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { return nil, nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) From dc0db09ac53b27d5041ea12974a66710782ae6fb Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 30 Oct 2023 11:18:41 +0530 Subject: [PATCH 106/263] fix --- cmd/cli/config/config.go | 45 ++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 61a7cfb2..84499be9 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -22,26 +22,35 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { if err != nil { return nil, nil, err } - var conns clickhouse.Options + //var conn clickhouse.Options - if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { - fmt.Println("Using provided username and password") - conns = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: cfg.ClickHouseUsername, - Password: cfg.ClickHousePassword, - }, - } - } else { - fmt.Println("Using connection without username and password") - conns = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - } - } + conn := clickhouse.OpenDB(&clickhouse.Options{ + Addr: []string{ + fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: cfg.ClickHouseUsername, + Password: cfg.ClickHousePassword, + }, + }) + // if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { + // fmt.Println("Using provided username and password") + // conn = clickhouse.Options{ + // Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + // Debug: true, + // Auth: clickhouse.Auth{ + // Username: cfg.ClickHouseUsername, + // Password: cfg.ClickHousePassword, + // }, + // } + // } else { + // fmt.Println("Using connection without username and password") + // conn = clickhouse.Options{ + // Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, + // } + // } - conn := clickhouse.OpenDB(&conns) + // conn = clickhouse.OpenDB(&conn) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { return nil, nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) From db2f20765ad8bd573a0816842e81f90294cc4e1e Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 30 Oct 2023 11:33:51 +0530 Subject: [PATCH 107/263] fix --- cmd/cli/config/config.go | 45 ++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 84499be9..61a7cfb2 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -22,35 +22,26 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { if err != nil { return nil, nil, err } - //var conn clickhouse.Options + var conns clickhouse.Options - conn := clickhouse.OpenDB(&clickhouse.Options{ - Addr: []string{ - fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: cfg.ClickHouseUsername, - Password: cfg.ClickHousePassword, - }, - }) - // if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { - // fmt.Println("Using provided username and password") - // conn = clickhouse.Options{ - // Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - // Debug: true, - // Auth: clickhouse.Auth{ - // Username: cfg.ClickHouseUsername, - // Password: cfg.ClickHousePassword, - // }, - // } - // } else { - // fmt.Println("Using connection without username and password") - // conn = clickhouse.Options{ - // Addr: []string{fmt.Sprintf("%s:%d", conf.DBAddress, conf.DbPort)}, - // } - // } + if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { + fmt.Println("Using provided username and password") + conns = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: cfg.ClickHouseUsername, + Password: cfg.ClickHousePassword, + }, + } + } else { + fmt.Println("Using connection without username and password") + conns = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + } + } - // conn = clickhouse.OpenDB(&conn) + conn := clickhouse.OpenDB(&conns) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { return nil, nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) From 09d6441b2ad2049b0fe73b4adf2a956abd9f0033 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 30 Oct 2023 14:07:20 +0530 Subject: [PATCH 108/263] fix --- cmd/cli/config/config.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 61a7cfb2..69d709df 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -22,11 +22,11 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { if err != nil { return nil, nil, err } - var conns clickhouse.Options + var options clickhouse.Options if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { fmt.Println("Using provided username and password") - conns = clickhouse.Options{ + options = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, Debug: true, Auth: clickhouse.Auth{ @@ -36,12 +36,12 @@ func OpenClickHouseConn() (*sql.DB, *Config, error) { } } else { fmt.Println("Using connection without username and password") - conns = clickhouse.Options{ + options = clickhouse.Options{ Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, } } - conn := clickhouse.OpenDB(&conns) + conn := clickhouse.OpenDB(&options) if err := conn.Ping(); err != nil { if exception, ok := err.(*clickhouse.Exception); ok { return nil, nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) From 72d60af5ea485bbdd3ce4d12a74fd29b73d180a1 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Thu, 2 Nov 2023 14:12:13 +0530 Subject: [PATCH 109/263] removed unwanted code --- client/pkg/clickhouse/db_client.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index cd910e3b..e517ebd1 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -99,12 +99,6 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { return nil, err } - // tables := []DBStatement{kubvizTable, rakeesTable, kubePugDepricatedTable, kubepugDeletedTable, ketallTable, trivyTableImage, trivySbomTable, outdateTable, clickhouseExperimental, containerGithubTable, kubescoreTable, trivyTableVul, trivyTableMisconfig, dockerHubBuildTable, azureContainerPushEventTable, quayContainerPushEventTable, jfrogContainerPushEventTable, DBStatement(dbstatement.AzureDevopsTable), DBStatement(dbstatement.GithubTable), DBStatement(dbstatement.GitlabTable), DBStatement(dbstatement.BitbucketTable), DBStatement(dbstatement.GiteaTable)} - // for _, table := range tables { - // if err = splconn.Exec(context.Background(), string(table)); err != nil { - // return nil, err - // } - // } var connOption clickhouse.Options if conf.ClickHouseUsername != "" && conf.ClickHousePassword != "" { From f74a7453aee1593c8aff1220b7b4ca4dcfb13328 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Thu, 2 Nov 2023 14:36:54 +0530 Subject: [PATCH 110/263] added ttl --- sql/20230915101223_events.up.sql | 7 +++++-- sql/20230915101317_rakkess.up.sql | 8 ++++++-- sql/20230915101358_DeprecatedAPIs.up.sql | 8 ++++++-- sql/20230915101437_DeletedAPIs.up.sql | 7 +++++-- sql/20230915101512_jfrogcontainerpush.up.sql | 8 ++++++-- sql/20230915101549_getall_resources.up.sql | 8 ++++++-- sql/20230915101643_outdated_images.up.sql | 8 ++++++-- sql/20230915101736_kubescore.up.sql | 8 ++++++-- sql/20230915101811_trivy_vul.up.sql | 8 ++++++-- sql/20230915101844_trivy_misconfig.up.sql | 8 ++++++-- sql/20230915101910_trivyimage.up.sql | 7 +++++-- sql/20230915102122_dockerhubbuild.up.sql | 7 +++++-- sql/20230915102157_azurecontainerpush.up.sql | 7 +++++-- sql/20230915102229_quaycontainerpush.up.sql | 7 +++++-- sql/20230915102314_trivysbom.up.sql | 7 +++++-- sql/20230915102348_azure_devops.up.sql | 7 +++++-- sql/20230915102437_github.up.sql | 7 +++++-- sql/20230915102739_gitlab.up.sql | 7 +++++-- sql/20230915102817_bitbucket.up.sql | 7 +++++-- sql/20230915102843_gitea.up.sql | 7 +++++-- 20 files changed, 108 insertions(+), 40 deletions(-) diff --git a/sql/20230915101223_events.up.sql b/sql/20230915101223_events.up.sql index b857a489..4d490012 100644 --- a/sql/20230915101223_events.up.sql +++ b/sql/20230915101223_events.up.sql @@ -11,5 +11,8 @@ CREATE TABLE IF NOT EXISTS events ( Host String, Event String, FirstTime String, - LastTime String -) engine=File(TabSeparated); + LastTime String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915101317_rakkess.up.sql b/sql/20230915101317_rakkess.up.sql index e297366c..2d7cc079 100644 --- a/sql/20230915101317_rakkess.up.sql +++ b/sql/20230915101317_rakkess.up.sql @@ -5,5 +5,9 @@ CREATE TABLE IF NOT EXISTS rakkess ( Delete String, List String, Update String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101358_DeprecatedAPIs.up.sql b/sql/20230915101358_DeprecatedAPIs.up.sql index 27dc8248..1bc1fdc4 100644 --- a/sql/20230915101358_DeprecatedAPIs.up.sql +++ b/sql/20230915101358_DeprecatedAPIs.up.sql @@ -5,5 +5,9 @@ CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( Kind String, Deprecated UInt8, Scope String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101437_DeletedAPIs.up.sql b/sql/20230915101437_DeletedAPIs.up.sql index 4a61f7a8..eaeddda3 100644 --- a/sql/20230915101437_DeletedAPIs.up.sql +++ b/sql/20230915101437_DeletedAPIs.up.sql @@ -7,5 +7,8 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( Name String, Deleted UInt8, Scope String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915101512_jfrogcontainerpush.up.sql b/sql/20230915101512_jfrogcontainerpush.up.sql index 60c6f058..aa470b8b 100644 --- a/sql/20230915101512_jfrogcontainerpush.up.sql +++ b/sql/20230915101512_jfrogcontainerpush.up.sql @@ -8,5 +8,9 @@ CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( ImageName String, Tag String, Event String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101549_getall_resources.up.sql b/sql/20230915101549_getall_resources.up.sql index 2263f53c..5a2c2e17 100644 --- a/sql/20230915101549_getall_resources.up.sql +++ b/sql/20230915101549_getall_resources.up.sql @@ -4,5 +4,9 @@ CREATE TABLE IF NOT EXISTS getall_resources ( Kind String, Resource String, Age String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101643_outdated_images.up.sql b/sql/20230915101643_outdated_images.up.sql index 44e67bcf..36a839c3 100644 --- a/sql/20230915101643_outdated_images.up.sql +++ b/sql/20230915101643_outdated_images.up.sql @@ -6,5 +6,9 @@ CREATE TABLE IF NOT EXISTS outdated_images ( CurrentTag String, LatestVersion String, VersionsBehind Int64, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101736_kubescore.up.sql b/sql/20230915101736_kubescore.up.sql index 0f18bfb7..fde28707 100644 --- a/sql/20230915101736_kubescore.up.sql +++ b/sql/20230915101736_kubescore.up.sql @@ -3,5 +3,9 @@ CREATE TABLE IF NOT EXISTS kubescore ( namespace String, cluster_name String, recommendations String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101811_trivy_vul.up.sql b/sql/20230915101811_trivy_vul.up.sql index f141ff03..d2f8efd3 100644 --- a/sql/20230915101811_trivy_vul.up.sql +++ b/sql/20230915101811_trivy_vul.up.sql @@ -14,5 +14,9 @@ CREATE TABLE IF NOT EXISTS trivy_vul ( vul_title String, vul_severity String, vul_published_date DateTime('UTC'), - vul_last_modified_date DateTime('UTC') -) engine=File(TabSeparated); + vul_last_modified_date DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101844_trivy_misconfig.up.sql b/sql/20230915101844_trivy_misconfig.up.sql index 3971b40e..02ff2dae 100644 --- a/sql/20230915101844_trivy_misconfig.up.sql +++ b/sql/20230915101844_trivy_misconfig.up.sql @@ -14,5 +14,9 @@ CREATE TABLE IF NOT EXISTS trivy_misconfig ( misconfig_resolution String, misconfig_severity String, misconfig_status String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; + diff --git a/sql/20230915101910_trivyimage.up.sql b/sql/20230915101910_trivyimage.up.sql index f6781862..e2a7aa04 100644 --- a/sql/20230915101910_trivyimage.up.sql +++ b/sql/20230915101910_trivyimage.up.sql @@ -10,5 +10,8 @@ CREATE TABLE IF NOT EXISTS trivyimage ( vul_title String, vul_severity String, vul_published_date DateTime('UTC'), - vul_last_modified_date DateTime('UTC') -) engine=File(TabSeparated); + vul_last_modified_date DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102122_dockerhubbuild.up.sql b/sql/20230915102122_dockerhubbuild.up.sql index 5d770afd..a9d0b5c1 100644 --- a/sql/20230915102122_dockerhubbuild.up.sql +++ b/sql/20230915102122_dockerhubbuild.up.sql @@ -5,5 +5,8 @@ CREATE TABLE IF NOT EXISTS dockerhubbuild ( DateCreated String, Owner String, Event String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102157_azurecontainerpush.up.sql b/sql/20230915102157_azurecontainerpush.up.sql index f603a09c..8bc4a1fc 100644 --- a/sql/20230915102157_azurecontainerpush.up.sql +++ b/sql/20230915102157_azurecontainerpush.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS azurecontainerpush ( Event String, Size Int32, SHAID String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; \ No newline at end of file diff --git a/sql/20230915102229_quaycontainerpush.up.sql b/sql/20230915102229_quaycontainerpush.up.sql index c79d0108..cf9c0a6e 100644 --- a/sql/20230915102229_quaycontainerpush.up.sql +++ b/sql/20230915102229_quaycontainerpush.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS quaycontainerpush ( homePage String, tag String, Event String, - EventTime DateTime('UTC') -) engine=File(TabSeparated); + EventTime DateTime('UTC'), + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102314_trivysbom.up.sql b/sql/20230915102314_trivysbom.up.sql index 4efa46a3..969f1b66 100644 --- a/sql/20230915102314_trivysbom.up.sql +++ b/sql/20230915102314_trivysbom.up.sql @@ -19,5 +19,8 @@ CREATE TABLE IF NOT EXISTS trivysbom ( component_hash_content String, component_license_exp String, component_purl String, - dependency_ref String -) engine=File(TabSeparated); + dependency_ref String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102348_azure_devops.up.sql b/sql/20230915102348_azure_devops.up.sql index 00f561b8..ddd02f2b 100644 --- a/sql/20230915102348_azure_devops.up.sql +++ b/sql/20230915102348_azure_devops.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS azure_devops ( EventType String, RepoName String, TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); + Event String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102437_github.up.sql b/sql/20230915102437_github.up.sql index 3adb6d79..00b4d3ac 100644 --- a/sql/20230915102437_github.up.sql +++ b/sql/20230915102437_github.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS github ( EventType String, RepoName String, TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); + Event String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102739_gitlab.up.sql b/sql/20230915102739_gitlab.up.sql index 737c66d4..d448fa48 100644 --- a/sql/20230915102739_gitlab.up.sql +++ b/sql/20230915102739_gitlab.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS gitlab ( EventType String, RepoName String, TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); + Event String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102817_bitbucket.up.sql b/sql/20230915102817_bitbucket.up.sql index d8f70809..4a5de37e 100644 --- a/sql/20230915102817_bitbucket.up.sql +++ b/sql/20230915102817_bitbucket.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS bitbucket ( EventType String, RepoName String, TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); + Event String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; diff --git a/sql/20230915102843_gitea.up.sql b/sql/20230915102843_gitea.up.sql index e8ef86fd..cc038088 100644 --- a/sql/20230915102843_gitea.up.sql +++ b/sql/20230915102843_gitea.up.sql @@ -6,5 +6,8 @@ CREATE TABLE IF NOT EXISTS gitea ( EventType String, RepoName String, TimeStamp DateTime('UTC'), - Event String -) engine=File(TabSeparated); + Event String, + ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; From c57314e0c1c2085172a84ef9bb813b266a9618bf Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sun, 5 Nov 2023 09:04:53 +0530 Subject: [PATCH 111/263] Add postgres support for grafana --- charts/client/Chart.yaml | 7 ++++++- charts/client/values.yaml | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 54675f1b..120a577f 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.7 +version: 1.1.8 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to @@ -31,7 +31,12 @@ dependencies: condition: clickhouse.enabled version: 1.0.2 repository: https://intelops.github.io/kubviz/ + - name: postgresql + condition: postgresql.enabled + version: 1.0.0 + repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: grafana condition: grafana.enabled version: 1.0.3 repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ + diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 3b4f7fde..4e18ab4d 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -98,12 +98,54 @@ clickhouse: clickhouse: replicas: "1" +postgresql: + enabled: false + auth: + ## @param auth.enablePostgresUser Assign a password to the "postgres" admin user. Otherwise, remote access will be blocked for this user + ## + enablePostgresUser: true + ## @param auth.postgresPassword Password for the "postgres" admin user. Ignored if `auth.existingSecret` is provided + ## + postgresPassword: "" + ## @param auth.username Name for a custom user to create + ## + username: "" + ## @param auth.password Password for the custom user to create. Ignored if `auth.existingSecret` is provided + ## + password: "" + ## @param auth.database Name for a custom database to create + ## + database: "" + ## @param auth.existingSecret Name of existing secret to use for PostgreSQL credentials. `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret. The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and picked from this secret in this case. + ## + existingSecret: "" + metrics: + enabled: false + grafana: enabled: false plugins: - vertamedia-clickhouse-datasource - grafana-clickhouse-datasource - volkovlabs-echarts-panel + sidecar: + dashboards: + provider: + allowUiUpdates: true + # grafana.ini: + # database: + # enabled: true + # type: postgres + # host: kubviz-client-postgresql:5432 + # name: postgres + # ssl_mode: disable + # user: postgres + # password: $__file{/etc/secrets/postgresql/postgres-password} + # extraSecretMounts: + # - name: my-postgresql-mount + # mountPath: /etc/secrets/postgresql + # secretName: kubviz-client-postgresql + # readOnly: true dashboards: enabled: true From 279760f7d7e5d85ffad8af4c2ca4ce99328a49c7 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Sun, 5 Nov 2023 10:26:11 +0530 Subject: [PATCH 112/263] modified migration with ttl with ttl interval as configurable --- cmd/cli/commands/sql.go | 27 ++----- cmd/cli/config/config.go | 39 ++-------- cmd/cli/config/utils.go | 123 +++++++++++++++++++++++++++++++ dockerfiles/migration/Dockerfile | 2 +- 4 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 cmd/cli/config/utils.go diff --git a/cmd/cli/commands/sql.go b/cmd/cli/commands/sql.go index 07389422..c9d0d610 100644 --- a/cmd/cli/commands/sql.go +++ b/cmd/cli/commands/sql.go @@ -5,8 +5,6 @@ import ( "log" "os" - "github.com/golang-migrate/migrate/v4" - cm "github.com/golang-migrate/migrate/v4/database/clickhouse" _ "github.com/golang-migrate/migrate/v4/source/file" "github.com/intelops/kubviz/cmd/cli/config" "github.com/spf13/cobra" @@ -47,28 +45,15 @@ migration sql -e --no`, if executeFlag { if yesFlag { - db, cfg, err := config.OpenClickHouseConn() + cfg, err := config.New() if err != nil { - log.Fatalf("Failed to open ClickHouse connection: %v", err) + log.Fatalf("failed to parse the env : %v", err.Error()) + return } - defer db.Close() - driver, err := cm.WithInstance(db, &cm.Config{}) - if err != nil { - log.Fatalf("Failed to create migrate driver: %v", err) - } - - m, err := migrate.NewWithDatabaseInstance( - fmt.Sprintf("file://%s", cfg.SchemaPath), - "clickhouse", - driver, - ) - if err != nil { - log.Fatalf("Clickhouse Migration initialization failed: %v", err) - } - if err := m.Up(); err != nil && err != migrate.ErrNoChange { - log.Fatalf("Migration failed: %v", err) + if err := cfg.Migrate(); err != nil { + log.Fatalf("failed to migrate : %v", err.Error()) + return } - fmt.Println("Clickhouse Migrations applied successfully!") } else { fmt.Println("Clickhouse Migration skipped due to --no flag.") } diff --git a/cmd/cli/config/config.go b/cmd/cli/config/config.go index 69d709df..7862f1c3 100644 --- a/cmd/cli/config/config.go +++ b/cmd/cli/config/config.go @@ -1,10 +1,6 @@ package config import ( - "database/sql" - "fmt" - - "github.com/ClickHouse/clickhouse-go/v2" "github.com/kelseyhightower/envconfig" ) @@ -14,40 +10,15 @@ type Config struct { ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` SchemaPath string `envconfig:"SCHEMA_PATH" default:"/sql"` + TtlInterval string `envconfig:"TTL_INTERVAL" default:"1"` + TtlUnit string `envconfig:"TTL_UNIT" default:"MONTH"` } -func OpenClickHouseConn() (*sql.DB, *Config, error) { +func New() (*Config, error) { var cfg Config err := envconfig.Process("", &cfg) if err != nil { - return nil, nil, err - } - var options clickhouse.Options - - if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { - fmt.Println("Using provided username and password") - options = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - Debug: true, - Auth: clickhouse.Auth{ - Username: cfg.ClickHouseUsername, - Password: cfg.ClickHousePassword, - }, - } - } else { - fmt.Println("Using connection without username and password") - options = clickhouse.Options{ - Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, - } - } - - conn := clickhouse.OpenDB(&options) - if err := conn.Ping(); err != nil { - if exception, ok := err.(*clickhouse.Exception); ok { - return nil, nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) - } else { - return nil, nil, err - } + return nil, err } - return conn, &cfg, nil + return &cfg, nil } diff --git a/cmd/cli/config/utils.go b/cmd/cli/config/utils.go new file mode 100644 index 00000000..360fbadc --- /dev/null +++ b/cmd/cli/config/utils.go @@ -0,0 +1,123 @@ +package config + +import ( + "bytes" + "database/sql" + "fmt" + "html/template" + "io" + "os" + "path/filepath" + "strings" + + "github.com/ClickHouse/clickhouse-go/v2" + "github.com/golang-migrate/migrate/v4" + ch "github.com/golang-migrate/migrate/v4/database/clickhouse" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +func (cfg *Config) openClickHouseConn() (*sql.DB, error) { + + var options clickhouse.Options + if cfg.ClickHouseUsername != "" && cfg.ClickHousePassword != "" { + fmt.Println("Using provided username and password") + options = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + Debug: true, + Auth: clickhouse.Auth{ + Username: cfg.ClickHouseUsername, + Password: cfg.ClickHousePassword, + }, + } + + } else { + fmt.Println("Using connection without username and password") + options = clickhouse.Options{ + Addr: []string{fmt.Sprintf("%s:%d", cfg.DBAddress, cfg.DbPort)}, + } + } + + conn := clickhouse.OpenDB(&options) + if err := conn.Ping(); err != nil { + if exception, ok := err.(*clickhouse.Exception); ok { + return nil, fmt.Errorf("[%d] %s %s", exception.Code, exception.Message, exception.StackTrace) + } else { + return nil, err + } + } + return conn, nil +} + +func (cfg *Config) processSQLTemplate(filePath string) (string, error) { + + file, err := os.Open(filePath) + if err != nil { + return "", err + } + data, err := io.ReadAll(file) + if err != nil { + return "", err + } + tmpl, err := template.New("sql").Parse(string(data)) + if err != nil { + return "", err + } + + params := map[string]string{ + "TTLValue": cfg.TtlInterval, + "TTLUnit": cfg.TtlUnit, + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, params) + if err != nil { + return "", err + } + return buf.String(), nil +} + +func (cfg *Config) Migrate() error { + dir := cfg.SchemaPath + files, err := os.ReadDir(dir) + if err != nil { + return fmt.Errorf("failed to read directory %w", err) + } + + for _, file := range files { + if strings.HasSuffix(file.Name(), ".up.sql") { + fullPath := filepath.Join(dir, file.Name()) + processedSQL, err := cfg.processSQLTemplate(fullPath) + if err != nil { + return fmt.Errorf("failed to process the sql template for file %s : %w", file.Name(), err) + } + + err = os.WriteFile(fullPath, []byte(processedSQL), 0644) + if err != nil { + return fmt.Errorf("failed to write to file %s : %w", fullPath, err) + } + } + } + + conn, err := cfg.openClickHouseConn() + if err != nil { + return fmt.Errorf("unable to create a clickhouse conection %w", err) + } + + driver, err := ch.WithInstance(conn, &ch.Config{}) + if err != nil { + return fmt.Errorf("failed to create migrate driver %w", err) + } + m, err := migrate.NewWithDatabaseInstance( + fmt.Sprintf("file://%s", dir), + "clickhouse", + driver, + ) + if err != nil { + return fmt.Errorf("clickhouse migration initialization failed %w", err) + } + if err := m.Up(); err != nil && err != migrate.ErrNoChange { + return fmt.Errorf("migration failed %w", err) + } + fmt.Println("Clickhouse Migration applied successfully!") + return nil +} diff --git a/dockerfiles/migration/Dockerfile b/dockerfiles/migration/Dockerfile index f2aa1bea..6cc51b93 100644 --- a/dockerfiles/migration/Dockerfile +++ b/dockerfiles/migration/Dockerfile @@ -16,7 +16,7 @@ WORKDIR / COPY --from=builder /workspace/migration . COPY --from=builder /workspace/sql /sql COPY --from=builder /workspace/script /script - +RUN chmod -R 777 /sql USER 65532:65532 ENTRYPOINT ["/migration"] From 831fe390a9744f1148f1904151985174d7febd77 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Sun, 5 Nov 2023 11:13:10 +0530 Subject: [PATCH 113/263] ttl-readme-added --- README.md | 12 +++++++++++- docs/CONFIGURATION_TTL.md | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 docs/CONFIGURATION_TTL.md diff --git a/README.md b/README.md index 0912d993..cc832512 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,17 @@ export POD_NAME=$(kubectl get pods --namespace kubviz -l "app.kubernetes.io/name kubectl --namespace kubviz port-forward $POD_NAME 3000 ``` -3. Access "localhost:3000" in your web browser, where you'll be prompted to enter your credentials. Utilize the username "admin" and the password obtained from step 1 to proceed. +3. Access "localhost:3000" in your web browser, where you'll be prompted to enter your credentials. Utilize the username "admin" and the password obtained from step 1 to proceed. + +#### TTL - Time-To-Live Feature + +We've implemented a Time-To-Live (TTL) feature to streamline the management of data within your ClickHouse tables. With TTL, historical data can be automatically relocated to alternative storage or purged to optimize storage space. This feature is particularly valuable for scenarios like time-series data or logs where older data gradually loses its relevance over time. + +#### Configuring TTL + +The TTL value is customizable, empowering you to define the specific duration after which data is marked as 'expired'. + +To guide you through the process of setting up a TTL, [please follow these steps](docs/CONFIGURATION_TTL.md) ## Use Cases diff --git a/docs/CONFIGURATION_TTL.md b/docs/CONFIGURATION_TTL.md new file mode 100644 index 00000000..cf4a165e --- /dev/null +++ b/docs/CONFIGURATION_TTL.md @@ -0,0 +1,17 @@ +# Configuring TTL: Guidelines and Instructions + +- **TTL_INTERVAL**: This parameter sets the numeric value for the TTL duration. For instance, if you wish for data to expire after a duration of 2 time units, set this value to 2. The default value is 1. + +- **TTL_UNIT**: This parameter specifies the time unit for the TTL duration. It accepts valid values such as SECOND, MINUTE, HOUR, DAY, MONTH, and more. For example, to set a TTL of 2 hours, you would set TTL_INTERVAL to 2 and TTL_UNIT to HOUR. The default unit is MONTH. + +# Usage + +## Setting Environment Variables + +To configure TTL for your application, set the desired environment variables. Here's an example of how to do this: + +```bash +export TTL_INTERVAL=5 +export TTL_UNIT=MINUTE +``` + From c479f42f442f24616a1f2dafb1f08f9a009297f2 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Sun, 5 Nov 2023 11:17:52 +0530 Subject: [PATCH 114/263] added ttl values --- ...rivy_misconfig.down.sql => 0000010_trivy_misconfig.down.sql} | 0 ...44_trivy_misconfig.up.sql => 0000010_trivy_misconfig.up.sql} | 2 +- ...15101910_trivyimage.down.sql => 0000011_trivyimage.down.sql} | 0 ...230915101910_trivyimage.up.sql => 0000011_trivyimage.up.sql} | 2 +- ..._dockerhubbuild.down.sql => 0000012_dockerhubbuild.down.sql} | 0 ...2122_dockerhubbuild.up.sql => 0000012_dockerhubbuild.up.sql} | 2 +- ...ntainerpush.down.sql => 0000013_azurecontainerpush.down.sql} | 0 ...recontainerpush.up.sql => 0000013_azurecontainerpush.up.sql} | 2 +- ...ontainerpush.down.sql => 0000014_quaycontainerpush.down.sql} | 0 ...uaycontainerpush.up.sql => 0000014_quaycontainerpush.up.sql} | 2 +- ...0915102314_trivysbom.down.sql => 0000015_trivysbom.down.sql} | 0 ...20230915102314_trivysbom.up.sql => 0000015_trivysbom.up.sql} | 2 +- ...2348_azure_devops.down.sql => 0000016_azure_devops.down.sql} | 0 ...15102348_azure_devops.up.sql => 0000016_azure_devops.up.sql} | 2 +- sql/{20230915102437_github.down.sql => 0000017_github.down.sql} | 0 sql/{20230915102437_github.up.sql => 0000017_github.up.sql} | 2 +- sql/{20230915102739_gitlab.down.sql => 0000018_gitlab.down.sql} | 0 sql/{20230915102739_gitlab.up.sql => 0000018_gitlab.up.sql} | 2 +- ...0915102817_bitbucket.down.sql => 0000019_bitbucket.down.sql} | 0 ...20230915102817_bitbucket.up.sql => 0000019_bitbucket.up.sql} | 2 +- sql/{20230915101223_events.down.sql => 000001_events.down.sql} | 0 sql/{20230915101223_events.up.sql => 000001_events.up.sql} | 2 +- sql/{20230915102843_gitea.down.sql => 0000020_gitea.down.sql} | 0 sql/{20230915102843_gitea.up.sql => 0000020_gitea.up.sql} | 2 +- ...{20230915101317_rakkess.down.sql => 000002_rakkess.down.sql} | 0 sql/{20230915101317_rakkess.up.sql => 000002_rakkess.up.sql} | 2 +- ...8_DeprecatedAPIs.down.sql => 000003_DeprecatedAPIs.down.sql} | 0 ...01358_DeprecatedAPIs.up.sql => 000003_DeprecatedAPIs.up.sql} | 2 +- ...5101437_DeletedAPIs.down.sql => 000004_DeletedAPIs.down.sql} | 0 ...30915101437_DeletedAPIs.up.sql => 000004_DeletedAPIs.up.sql} | 2 +- ...ontainerpush.down.sql => 000005_jfrogcontainerpush.down.sql} | 0 ...rogcontainerpush.up.sql => 000005_jfrogcontainerpush.up.sql} | 2 +- ...tall_resources.down.sql => 000006_getall_resources.down.sql} | 0 ...9_getall_resources.up.sql => 000006_getall_resources.up.sql} | 2 +- ...outdated_images.down.sql => 000007_outdated_images.down.sql} | 0 ...643_outdated_images.up.sql => 000007_outdated_images.up.sql} | 2 +- ...30915101736_kubescore.down.sql => 000008_kubescore.down.sql} | 0 ...{20230915101736_kubescore.up.sql => 000008_kubescore.up.sql} | 2 +- ...30915101811_trivy_vul.down.sql => 000009_trivy_vul.down.sql} | 0 ...{20230915101811_trivy_vul.up.sql => 000009_trivy_vul.up.sql} | 2 +- 40 files changed, 20 insertions(+), 20 deletions(-) rename sql/{20230915101844_trivy_misconfig.down.sql => 0000010_trivy_misconfig.down.sql} (100%) rename sql/{20230915101844_trivy_misconfig.up.sql => 0000010_trivy_misconfig.up.sql} (88%) rename sql/{20230915101910_trivyimage.down.sql => 0000011_trivyimage.down.sql} (100%) rename sql/{20230915101910_trivyimage.up.sql => 0000011_trivyimage.up.sql} (86%) rename sql/{20230915102122_dockerhubbuild.down.sql => 0000012_dockerhubbuild.down.sql} (100%) rename sql/{20230915102122_dockerhubbuild.up.sql => 0000012_dockerhubbuild.up.sql} (79%) rename sql/{20230915102157_azurecontainerpush.down.sql => 0000013_azurecontainerpush.down.sql} (100%) rename sql/{20230915102157_azurecontainerpush.up.sql => 0000013_azurecontainerpush.up.sql} (80%) rename sql/{20230915102229_quaycontainerpush.down.sql => 0000014_quaycontainerpush.down.sql} (100%) rename sql/{20230915102229_quaycontainerpush.up.sql => 0000014_quaycontainerpush.up.sql} (80%) rename sql/{20230915102314_trivysbom.down.sql => 0000015_trivysbom.down.sql} (100%) rename sql/{20230915102314_trivysbom.up.sql => 0000015_trivysbom.up.sql} (91%) rename sql/{20230915102348_azure_devops.down.sql => 0000016_azure_devops.down.sql} (100%) rename sql/{20230915102348_azure_devops.up.sql => 0000016_azure_devops.up.sql} (79%) rename sql/{20230915102437_github.down.sql => 0000017_github.down.sql} (100%) rename sql/{20230915102437_github.up.sql => 0000017_github.up.sql} (79%) rename sql/{20230915102739_gitlab.down.sql => 0000018_gitlab.down.sql} (100%) rename sql/{20230915102739_gitlab.up.sql => 0000018_gitlab.up.sql} (79%) rename sql/{20230915102817_bitbucket.down.sql => 0000019_bitbucket.down.sql} (100%) rename sql/{20230915102817_bitbucket.up.sql => 0000019_bitbucket.up.sql} (79%) rename sql/{20230915101223_events.down.sql => 000001_events.down.sql} (100%) rename sql/{20230915101223_events.up.sql => 000001_events.up.sql} (83%) rename sql/{20230915102843_gitea.down.sql => 0000020_gitea.down.sql} (100%) rename sql/{20230915102843_gitea.up.sql => 0000020_gitea.up.sql} (79%) rename sql/{20230915101317_rakkess.down.sql => 000002_rakkess.down.sql} (100%) rename sql/{20230915101317_rakkess.up.sql => 000002_rakkess.up.sql} (77%) rename sql/{20230915101358_DeprecatedAPIs.down.sql => 000003_DeprecatedAPIs.down.sql} (100%) rename sql/{20230915101358_DeprecatedAPIs.up.sql => 000003_DeprecatedAPIs.up.sql} (79%) rename sql/{20230915101437_DeletedAPIs.down.sql => 000004_DeletedAPIs.down.sql} (100%) rename sql/{20230915101437_DeletedAPIs.up.sql => 000004_DeletedAPIs.up.sql} (82%) rename sql/{20230915101512_jfrogcontainerpush.down.sql => 000005_jfrogcontainerpush.down.sql} (100%) rename sql/{20230915101512_jfrogcontainerpush.up.sql => 000005_jfrogcontainerpush.up.sql} (83%) rename sql/{20230915101549_getall_resources.down.sql => 000006_getall_resources.down.sql} (100%) rename sql/{20230915101549_getall_resources.up.sql => 000006_getall_resources.up.sql} (77%) rename sql/{20230915101643_outdated_images.down.sql => 000007_outdated_images.down.sql} (100%) rename sql/{20230915101643_outdated_images.up.sql => 000007_outdated_images.up.sql} (81%) rename sql/{20230915101736_kubescore.down.sql => 000008_kubescore.down.sql} (100%) rename sql/{20230915101736_kubescore.up.sql => 000008_kubescore.up.sql} (76%) rename sql/{20230915101811_trivy_vul.down.sql => 000009_trivy_vul.down.sql} (100%) rename sql/{20230915101811_trivy_vul.up.sql => 000009_trivy_vul.up.sql} (89%) diff --git a/sql/20230915101844_trivy_misconfig.down.sql b/sql/0000010_trivy_misconfig.down.sql similarity index 100% rename from sql/20230915101844_trivy_misconfig.down.sql rename to sql/0000010_trivy_misconfig.down.sql diff --git a/sql/20230915101844_trivy_misconfig.up.sql b/sql/0000010_trivy_misconfig.up.sql similarity index 88% rename from sql/20230915101844_trivy_misconfig.up.sql rename to sql/0000010_trivy_misconfig.up.sql index 02ff2dae..89d758a5 100644 --- a/sql/20230915101844_trivy_misconfig.up.sql +++ b/sql/0000010_trivy_misconfig.up.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS trivy_misconfig ( misconfig_severity String, misconfig_status String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101910_trivyimage.down.sql b/sql/0000011_trivyimage.down.sql similarity index 100% rename from sql/20230915101910_trivyimage.down.sql rename to sql/0000011_trivyimage.down.sql diff --git a/sql/20230915101910_trivyimage.up.sql b/sql/0000011_trivyimage.up.sql similarity index 86% rename from sql/20230915101910_trivyimage.up.sql rename to sql/0000011_trivyimage.up.sql index e2a7aa04..572bb2b4 100644 --- a/sql/20230915101910_trivyimage.up.sql +++ b/sql/0000011_trivyimage.up.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS trivyimage ( vul_severity String, vul_published_date DateTime('UTC'), vul_last_modified_date DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102122_dockerhubbuild.down.sql b/sql/0000012_dockerhubbuild.down.sql similarity index 100% rename from sql/20230915102122_dockerhubbuild.down.sql rename to sql/0000012_dockerhubbuild.down.sql diff --git a/sql/20230915102122_dockerhubbuild.up.sql b/sql/0000012_dockerhubbuild.up.sql similarity index 79% rename from sql/20230915102122_dockerhubbuild.up.sql rename to sql/0000012_dockerhubbuild.up.sql index a9d0b5c1..0485e52b 100644 --- a/sql/20230915102122_dockerhubbuild.up.sql +++ b/sql/0000012_dockerhubbuild.up.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS dockerhubbuild ( Owner String, Event String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102157_azurecontainerpush.down.sql b/sql/0000013_azurecontainerpush.down.sql similarity index 100% rename from sql/20230915102157_azurecontainerpush.down.sql rename to sql/0000013_azurecontainerpush.down.sql diff --git a/sql/20230915102157_azurecontainerpush.up.sql b/sql/0000013_azurecontainerpush.up.sql similarity index 80% rename from sql/20230915102157_azurecontainerpush.up.sql rename to sql/0000013_azurecontainerpush.up.sql index 8bc4a1fc..a0f5916d 100644 --- a/sql/20230915102157_azurecontainerpush.up.sql +++ b/sql/0000013_azurecontainerpush.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS azurecontainerpush ( Size Int32, SHAID String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; \ No newline at end of file diff --git a/sql/20230915102229_quaycontainerpush.down.sql b/sql/0000014_quaycontainerpush.down.sql similarity index 100% rename from sql/20230915102229_quaycontainerpush.down.sql rename to sql/0000014_quaycontainerpush.down.sql diff --git a/sql/20230915102229_quaycontainerpush.up.sql b/sql/0000014_quaycontainerpush.up.sql similarity index 80% rename from sql/20230915102229_quaycontainerpush.up.sql rename to sql/0000014_quaycontainerpush.up.sql index cf9c0a6e..2c249b56 100644 --- a/sql/20230915102229_quaycontainerpush.up.sql +++ b/sql/0000014_quaycontainerpush.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS quaycontainerpush ( tag String, Event String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102314_trivysbom.down.sql b/sql/0000015_trivysbom.down.sql similarity index 100% rename from sql/20230915102314_trivysbom.down.sql rename to sql/0000015_trivysbom.down.sql diff --git a/sql/20230915102314_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql similarity index 91% rename from sql/20230915102314_trivysbom.up.sql rename to sql/0000015_trivysbom.up.sql index 969f1b66..76de0994 100644 --- a/sql/20230915102314_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS trivysbom ( component_license_exp String, component_purl String, dependency_ref String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102348_azure_devops.down.sql b/sql/0000016_azure_devops.down.sql similarity index 100% rename from sql/20230915102348_azure_devops.down.sql rename to sql/0000016_azure_devops.down.sql diff --git a/sql/20230915102348_azure_devops.up.sql b/sql/0000016_azure_devops.up.sql similarity index 79% rename from sql/20230915102348_azure_devops.up.sql rename to sql/0000016_azure_devops.up.sql index ddd02f2b..06a32dd2 100644 --- a/sql/20230915102348_azure_devops.up.sql +++ b/sql/0000016_azure_devops.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS azure_devops ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102437_github.down.sql b/sql/0000017_github.down.sql similarity index 100% rename from sql/20230915102437_github.down.sql rename to sql/0000017_github.down.sql diff --git a/sql/20230915102437_github.up.sql b/sql/0000017_github.up.sql similarity index 79% rename from sql/20230915102437_github.up.sql rename to sql/0000017_github.up.sql index 00b4d3ac..96d9bf24 100644 --- a/sql/20230915102437_github.up.sql +++ b/sql/0000017_github.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS github ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102739_gitlab.down.sql b/sql/0000018_gitlab.down.sql similarity index 100% rename from sql/20230915102739_gitlab.down.sql rename to sql/0000018_gitlab.down.sql diff --git a/sql/20230915102739_gitlab.up.sql b/sql/0000018_gitlab.up.sql similarity index 79% rename from sql/20230915102739_gitlab.up.sql rename to sql/0000018_gitlab.up.sql index d448fa48..2403dff1 100644 --- a/sql/20230915102739_gitlab.up.sql +++ b/sql/0000018_gitlab.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS gitlab ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102817_bitbucket.down.sql b/sql/0000019_bitbucket.down.sql similarity index 100% rename from sql/20230915102817_bitbucket.down.sql rename to sql/0000019_bitbucket.down.sql diff --git a/sql/20230915102817_bitbucket.up.sql b/sql/0000019_bitbucket.up.sql similarity index 79% rename from sql/20230915102817_bitbucket.up.sql rename to sql/0000019_bitbucket.up.sql index 4a5de37e..adf08956 100644 --- a/sql/20230915102817_bitbucket.up.sql +++ b/sql/0000019_bitbucket.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS bitbucket ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101223_events.down.sql b/sql/000001_events.down.sql similarity index 100% rename from sql/20230915101223_events.down.sql rename to sql/000001_events.down.sql diff --git a/sql/20230915101223_events.up.sql b/sql/000001_events.up.sql similarity index 83% rename from sql/20230915101223_events.up.sql rename to sql/000001_events.up.sql index 4d490012..a410aaf5 100644 --- a/sql/20230915101223_events.up.sql +++ b/sql/000001_events.up.sql @@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS events ( Event String, FirstTime String, LastTime String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915102843_gitea.down.sql b/sql/0000020_gitea.down.sql similarity index 100% rename from sql/20230915102843_gitea.down.sql rename to sql/0000020_gitea.down.sql diff --git a/sql/20230915102843_gitea.up.sql b/sql/0000020_gitea.up.sql similarity index 79% rename from sql/20230915102843_gitea.up.sql rename to sql/0000020_gitea.up.sql index cc038088..1b42c0e7 100644 --- a/sql/20230915102843_gitea.up.sql +++ b/sql/0000020_gitea.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS gitea ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101317_rakkess.down.sql b/sql/000002_rakkess.down.sql similarity index 100% rename from sql/20230915101317_rakkess.down.sql rename to sql/000002_rakkess.down.sql diff --git a/sql/20230915101317_rakkess.up.sql b/sql/000002_rakkess.up.sql similarity index 77% rename from sql/20230915101317_rakkess.up.sql rename to sql/000002_rakkess.up.sql index 2d7cc079..3542f59b 100644 --- a/sql/20230915101317_rakkess.up.sql +++ b/sql/000002_rakkess.up.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS rakkess ( List String, Update String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101358_DeprecatedAPIs.down.sql b/sql/000003_DeprecatedAPIs.down.sql similarity index 100% rename from sql/20230915101358_DeprecatedAPIs.down.sql rename to sql/000003_DeprecatedAPIs.down.sql diff --git a/sql/20230915101358_DeprecatedAPIs.up.sql b/sql/000003_DeprecatedAPIs.up.sql similarity index 79% rename from sql/20230915101358_DeprecatedAPIs.up.sql rename to sql/000003_DeprecatedAPIs.up.sql index 1bc1fdc4..a7ed1b6e 100644 --- a/sql/20230915101358_DeprecatedAPIs.up.sql +++ b/sql/000003_DeprecatedAPIs.up.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( Deprecated UInt8, Scope String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101437_DeletedAPIs.down.sql b/sql/000004_DeletedAPIs.down.sql similarity index 100% rename from sql/20230915101437_DeletedAPIs.down.sql rename to sql/000004_DeletedAPIs.down.sql diff --git a/sql/20230915101437_DeletedAPIs.up.sql b/sql/000004_DeletedAPIs.up.sql similarity index 82% rename from sql/20230915101437_DeletedAPIs.up.sql rename to sql/000004_DeletedAPIs.up.sql index eaeddda3..931b93e7 100644 --- a/sql/20230915101437_DeletedAPIs.up.sql +++ b/sql/000004_DeletedAPIs.up.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( Deleted UInt8, Scope String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101512_jfrogcontainerpush.down.sql b/sql/000005_jfrogcontainerpush.down.sql similarity index 100% rename from sql/20230915101512_jfrogcontainerpush.down.sql rename to sql/000005_jfrogcontainerpush.down.sql diff --git a/sql/20230915101512_jfrogcontainerpush.up.sql b/sql/000005_jfrogcontainerpush.up.sql similarity index 83% rename from sql/20230915101512_jfrogcontainerpush.up.sql rename to sql/000005_jfrogcontainerpush.up.sql index aa470b8b..6bac6d8d 100644 --- a/sql/20230915101512_jfrogcontainerpush.up.sql +++ b/sql/000005_jfrogcontainerpush.up.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( Tag String, Event String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101549_getall_resources.down.sql b/sql/000006_getall_resources.down.sql similarity index 100% rename from sql/20230915101549_getall_resources.down.sql rename to sql/000006_getall_resources.down.sql diff --git a/sql/20230915101549_getall_resources.up.sql b/sql/000006_getall_resources.up.sql similarity index 77% rename from sql/20230915101549_getall_resources.up.sql rename to sql/000006_getall_resources.up.sql index 5a2c2e17..23991a21 100644 --- a/sql/20230915101549_getall_resources.up.sql +++ b/sql/000006_getall_resources.up.sql @@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS getall_resources ( Resource String, Age String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101643_outdated_images.down.sql b/sql/000007_outdated_images.down.sql similarity index 100% rename from sql/20230915101643_outdated_images.down.sql rename to sql/000007_outdated_images.down.sql diff --git a/sql/20230915101643_outdated_images.up.sql b/sql/000007_outdated_images.up.sql similarity index 81% rename from sql/20230915101643_outdated_images.up.sql rename to sql/000007_outdated_images.up.sql index 36a839c3..479c0902 100644 --- a/sql/20230915101643_outdated_images.up.sql +++ b/sql/000007_outdated_images.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS outdated_images ( LatestVersion String, VersionsBehind Int64, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101736_kubescore.down.sql b/sql/000008_kubescore.down.sql similarity index 100% rename from sql/20230915101736_kubescore.down.sql rename to sql/000008_kubescore.down.sql diff --git a/sql/20230915101736_kubescore.up.sql b/sql/000008_kubescore.up.sql similarity index 76% rename from sql/20230915101736_kubescore.up.sql rename to sql/000008_kubescore.up.sql index fde28707..662fab9b 100644 --- a/sql/20230915101736_kubescore.up.sql +++ b/sql/000008_kubescore.up.sql @@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS kubescore ( cluster_name String, recommendations String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/20230915101811_trivy_vul.down.sql b/sql/000009_trivy_vul.down.sql similarity index 100% rename from sql/20230915101811_trivy_vul.down.sql rename to sql/000009_trivy_vul.down.sql diff --git a/sql/20230915101811_trivy_vul.up.sql b/sql/000009_trivy_vul.up.sql similarity index 89% rename from sql/20230915101811_trivy_vul.up.sql rename to sql/000009_trivy_vul.up.sql index d2f8efd3..8acb241d 100644 --- a/sql/20230915101811_trivy_vul.up.sql +++ b/sql/000009_trivy_vul.up.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS trivy_vul ( vul_severity String, vul_published_date DateTime('UTC'), vul_last_modified_date DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL 6 MONTH + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; From ff1eb520caaec43a5b19bcf3cf317f929f76245d Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 7 Nov 2023 17:31:37 +0530 Subject: [PATCH 115/263] Add postgres support for grafana --- charts/client/Chart.yaml | 6 +---- charts/client/values.yaml | 52 +++++++++++---------------------------- 2 files changed, 15 insertions(+), 43 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 120a577f..1a6c114d 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -31,12 +31,8 @@ dependencies: condition: clickhouse.enabled version: 1.0.2 repository: https://intelops.github.io/kubviz/ - - name: postgresql - condition: postgresql.enabled - version: 1.0.0 - repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: grafana condition: grafana.enabled - version: 1.0.3 + version: 1.0.4 repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 4e18ab4d..6534c258 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -98,30 +98,6 @@ clickhouse: clickhouse: replicas: "1" -postgresql: - enabled: false - auth: - ## @param auth.enablePostgresUser Assign a password to the "postgres" admin user. Otherwise, remote access will be blocked for this user - ## - enablePostgresUser: true - ## @param auth.postgresPassword Password for the "postgres" admin user. Ignored if `auth.existingSecret` is provided - ## - postgresPassword: "" - ## @param auth.username Name for a custom user to create - ## - username: "" - ## @param auth.password Password for the custom user to create. Ignored if `auth.existingSecret` is provided - ## - password: "" - ## @param auth.database Name for a custom database to create - ## - database: "" - ## @param auth.existingSecret Name of existing secret to use for PostgreSQL credentials. `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret. The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and picked from this secret in this case. - ## - existingSecret: "" - metrics: - enabled: false - grafana: enabled: false plugins: @@ -132,20 +108,20 @@ grafana: dashboards: provider: allowUiUpdates: true - # grafana.ini: - # database: - # enabled: true - # type: postgres - # host: kubviz-client-postgresql:5432 - # name: postgres - # ssl_mode: disable - # user: postgres - # password: $__file{/etc/secrets/postgresql/postgres-password} - # extraSecretMounts: - # - name: my-postgresql-mount - # mountPath: /etc/secrets/postgresql - # secretName: kubviz-client-postgresql - # readOnly: true + postgresql: + enabled: false + database: + type: postgres + host: kubviz-client-postgresql:5432 + name: postgres + ssl_mode: disable + user: postgres + password: $__file{/etc/secrets/postgresql/postgres-password} + secretMount: + name: postgresql-mount + mountPath: /etc/secrets/postgresql + secretName: kubviz-client-postgresql + readOnly: true dashboards: enabled: true From cb9411054924e41a5c2f63c6f421bac25f908111 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 8 Nov 2023 11:17:01 +0530 Subject: [PATCH 116/263] sbom-fix --- agent/kubviz/trivy_sbom.go | 7 +-- client/pkg/clickhouse/db_client.go | 66 +++++++++-------------------- client/pkg/clickhouse/statements.go | 2 +- client/pkg/clients/kubviz_client.go | 2 +- model/trivy_sbom.go | 55 ++---------------------- sql/0000015_trivysbom.up.sql | 26 +++--------- 6 files changed, 38 insertions(+), 120 deletions(-) diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index c0b697aa..fe9c5cf8 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -7,6 +7,7 @@ import ( "log" "os/exec" + "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" "github.com/google/uuid" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" @@ -14,8 +15,8 @@ import ( "k8s.io/client-go/rest" ) -func publishTrivySbomReport(report model.Sbom, js nats.JetStreamContext) error { - metrics := model.Reports{ +func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { + metrics := model.Sbom{ ID: uuid.New().String(), Report: report, } @@ -67,7 +68,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image } - var report model.Sbom + var report cyclonedx.BOM err = json.Unmarshal(out, &report) if err != nil { log.Printf("Error unmarshaling JSON data for image sbom %s: %v", image.PullableImage, err) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index e517ebd1..82d3c1d0 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -33,7 +33,7 @@ type DBInterface interface { InsertGitEvent(string) InsertKubeScoreMetrics(model.KubeScoreRecommendations) InsertTrivyImageMetrics(metrics model.TrivyImage) - InsertTrivySbomMetrics(metrics model.Reports) + InsertTrivySbomMetrics(metrics model.Sbom) InsertTrivyMetrics(metrics model.Trivy) RetriveKetallEvent() ([]model.Resource, error) RetriveOutdatedEvent() ([]model.CheckResultfinal, error) @@ -600,55 +600,31 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { } } -func (c *DBClient) InsertTrivySbomMetrics(metrics model.Reports) { +func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { log.Println("####started inserting value") result := metrics.Report - tx, err := c.conn.Begin() - if err != nil { - log.Println("error in conn Begin", err) - } - defer tx.Rollback() - stmt, err := tx.Prepare(InsertTrivySbom) - if err != nil { - log.Println("error in prepare", err) - } - defer stmt.Close() - for _, com := range result.Components { - if len(result.Metadata.Tools) == 0 || len(com.Properties) == 0 || len(com.Hashes) == 0 || len(com.Licenses) == 0 { - continue - } - for _, depend := range result.Dependencies { - if _, err := stmt.Exec( - metrics.ID, - result.Schema, - result.BomFormat, - result.SpecVersion, - result.SerialNumber, - int32(result.Version), - result.Metadata.Timestamp, - result.Metadata.Tools[0].Vendor, - result.Metadata.Tools[0].Name, - result.Metadata.Tools[0].Version, - com.BomRef, - com.Type, - com.Name, - com.Version, - com.Properties[0].Name, - com.Properties[0].Value, - com.Hashes[0].Alg, - com.Hashes[0].Content, - com.Licenses[0].Expression, - com.Purl, - depend.Ref, - ); err != nil { - log.Fatal(err) - } - } + var ( + tx, _ = c.conn.Begin() + stmt, _ = tx.Prepare(InsertTrivySbom) + ) + if _,err:= stmt.Exec( + metrics.ID, + result.CycloneDX.Metadata.Component.Name, + result.CycloneDX.Metadata.Component.Version, + result.CycloneDX.Metadata.Component.PackageURL, + result.CycloneDX.Metadata.Component.MIMEType, + result.CycloneDX.Metadata.Component.BOMRef, + result.CycloneDX.SerialNumber, + result.CycloneDX.Version, + result.CycloneDX.BOMFormat, + ); err!=nil { + log.Fatal(err) } - if err := tx.Commit(); err != nil { + if err:=tx.Commit();err!=nil { log.Fatal(err) } - log.Println("value inserted") + stmt.Close() + } func (c *DBClient) Close() { _ = c.conn.Close() diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 8d2bb614..34aa1b92 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -242,6 +242,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, schema, bom_format,spec_version,serial_number, version, metadata_timestamp,metatool_vendor,metatool_name,metatool_version,component_bom_ref,component_type,component_name,component_version,component_property_name,component_property_value,component_hash_alg,component_hash_content,component_license_exp,component_purl,dependency_ref) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, image_version, package_url, mime_type, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index 394bf8a1..e2208a4a 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -118,7 +118,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { Consumer: constants.Trivy_Sbom_Consumer, Handler: func(msg *nats.Msg) { msg.Ack() - var metrics model.Reports + var metrics model.Sbom err := json.Unmarshal(msg.Data, &metrics) if err != nil { log.Println("failed to unmarshal in nats", err) diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index bcbe7219..c6e6c850 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -1,59 +1,12 @@ package model import ( - "time" + "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" ) -type Reports struct { +type Sbom struct { ID string - Report Sbom + Report cyclonedx.BOM } -type Sbom struct { - Schema string `json:"$schema"` - BomFormat string `json:"bomFormat"` - SpecVersion string `json:"specVersion"` - SerialNumber string `json:"serialNumber"` - Version int `json:"version"` - Metadata struct { - Timestamp time.Time `json:"timestamp"` - Tools []struct { - Vendor string `json:"vendor"` - Name string `json:"name"` - Version string `json:"version"` - } `json:"tools"` - Component struct { - BomRef string `json:"bom-ref"` - Type string `json:"type"` - Name string `json:"name"` - Purl string `json:"purl"` - Properties []struct { - Name string `json:"name"` - Value string `json:"value"` - } `json:"properties"` - } `json:"component"` - } `json:"metadata"` - Components []struct { - BomRef string `json:"bom-ref"` - Type string `json:"type"` - Name string `json:"name"` - Version string `json:"version"` - Properties []struct { - Name string `json:"name"` - Value string `json:"value"` - } `json:"properties"` - Hashes []struct { - Alg string `json:"alg"` - Content string `json:"content"` - } `json:"hashes,omitempty"` - Licenses []struct { - Expression string `json:"expression"` - } `json:"licenses,omitempty"` - Purl string `json:"purl,omitempty"` - } `json:"components"` - Dependencies []struct { - Ref string `json:"ref"` - DependsOn []string `json:"dependsOn"` - } `json:"dependencies"` - Vulnerabilities []interface{} `json:"vulnerabilities"` -} + diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index 76de0994..cf223a32 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -1,25 +1,13 @@ CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, - schema String, - bom_format String, - spec_version String, + image_name String, + image_version String, + package_url String, + mime_type String, + bom_ref String, serial_number String, - version INTEGER, - metadata_timestamp DateTime('UTC'), - metatool_vendor String, - metatool_name String, - metatool_version String, - component_bom_ref String, - component_type String, - component_name String, - component_version String, - component_property_name String, - component_property_value String, - component_hash_alg String, - component_hash_content String, - component_license_exp String, - component_purl String, - dependency_ref String, + version INTEGER + bom_format String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate From 5af755cd47f577bd8d736ed7f3fd93764e35b003 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 8 Nov 2023 14:13:17 +0530 Subject: [PATCH 117/263] pointer-nil-condition --- client/pkg/clickhouse/db_client.go | 43 +++++++++++++++++++++-------- client/pkg/clickhouse/statements.go | 2 +- sql/0000015_trivysbom.up.sql | 4 +-- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 82d3c1d0..f5a4e2d0 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -607,19 +607,38 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { tx, _ = c.conn.Begin() stmt, _ = tx.Prepare(InsertTrivySbom) ) - if _,err:= stmt.Exec( - metrics.ID, - result.CycloneDX.Metadata.Component.Name, - result.CycloneDX.Metadata.Component.Version, - result.CycloneDX.Metadata.Component.PackageURL, - result.CycloneDX.Metadata.Component.MIMEType, - result.CycloneDX.Metadata.Component.BOMRef, - result.CycloneDX.SerialNumber, - result.CycloneDX.Version, - result.CycloneDX.BOMFormat, - ); err!=nil { - log.Fatal(err) + if result.CycloneDX != nil { + if _,err:= stmt.Exec( + metrics.ID, + result.CycloneDX.Metadata.Component.Name, + result.CycloneDX.Metadata.Component.PackageURL, + result.CycloneDX.Metadata.Component.BOMRef, + result.CycloneDX.SerialNumber, + result.CycloneDX.Version, + result.CycloneDX.BOMFormat, + result.CycloneDX.Metadata.Component.Version, + result.CycloneDX.Metadata.Component.MIMEType, + ); err!=nil { + log.Fatal(err) + } + + }else { + if _,err:= stmt.Exec( + metrics.ID, + "-", + "-", + "-", + "-", + "-", + "-", + "-", + "-", + ); err!=nil { + log.Fatal(err) + } + } + if err:=tx.Commit();err!=nil { log.Fatal(err) } diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 34aa1b92..e1abea47 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -242,6 +242,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, image_version, package_url, mime_type, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, package_url, bom_ref, serial_number, version, bom_format, component_version, component_mimetype) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index cf223a32..ece47761 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -1,13 +1,13 @@ CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, image_name String, - image_version String, package_url String, - mime_type String, bom_ref String, serial_number String, version INTEGER bom_format String, + component_version String, + component_mime_type String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate From 4a0a60a846e7d1e9adef811fe91b2f7e15ae1d05 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 8 Nov 2023 14:54:36 +0530 Subject: [PATCH 118/263] available-values-inserting-db --- client/pkg/clickhouse/db_client.go | 31 +++++++++--------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index f5a4e2d0..be6210c2 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -603,11 +603,12 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { log.Println("####started inserting value") result := metrics.Report - var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(InsertTrivySbom) - ) + if result.CycloneDX != nil { + var ( + tx, _ = c.conn.Begin() + stmt, _ = tx.Prepare(InsertTrivySbom) + ) if _,err:= stmt.Exec( metrics.ID, result.CycloneDX.Metadata.Component.Name, @@ -621,29 +622,15 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { ); err!=nil { log.Fatal(err) } - - }else { - if _,err:= stmt.Exec( - metrics.ID, - "-", - "-", - "-", - "-", - "-", - "-", - "-", - "-", - ); err!=nil { + if err:=tx.Commit();err!=nil { log.Fatal(err) } + stmt.Close() + }else { + log.Println("No Data Available") } - if err:=tx.Commit();err!=nil { - log.Fatal(err) - } - stmt.Close() - } func (c *DBClient) Close() { _ = c.conn.Close() From 5b6988290f6b9b6b4ce6af2dd3430925d6f5b678 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 8 Nov 2023 14:59:45 +0530 Subject: [PATCH 119/263] printing-statement changed --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index be6210c2..7a9aaddb 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -627,7 +627,7 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { } stmt.Close() }else { - log.Println("No Data Available") + log.Println("sbom payload not available for db insertion, skipping db insertion") } From b9ae397c544e9b4d0b7046bd32688fd4d43fe2ad Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 8 Nov 2023 20:37:31 +0530 Subject: [PATCH 120/263] add support for external clickhouse connection --- charts/client/Chart.yaml | 2 +- .../configmap-clickhouse-datasource.yaml | 17 +++++++ .../configmap-vertamedia-datasource.yaml | 18 ++++++++ charts/client/templates/deployment.yaml | 44 +++++++++++++++++++ charts/client/values.yaml | 19 ++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 1a6c114d..a7d900bb 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -33,6 +33,6 @@ dependencies: repository: https://intelops.github.io/kubviz/ - name: grafana condition: grafana.enabled - version: 1.0.4 + version: 1.0.5 repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ diff --git a/charts/client/templates/configmap-clickhouse-datasource.yaml b/charts/client/templates/configmap-clickhouse-datasource.yaml index b28461cd..ddcc43d9 100644 --- a/charts/client/templates/configmap-clickhouse-datasource.yaml +++ b/charts/client/templates/configmap-clickhouse-datasource.yaml @@ -13,6 +13,23 @@ data: type: grafana-clickhouse-datasource jsonData: port: 9000 + {{- if .Values.clickhouse.enabled }} server: {{ include "client.fullname" . }}-clickhouse + {{- else }} + server: {{ .Values.existingClickhouse.host }} tlsSkipVerify: true + {{- if not .Values.clickhouse.enabled }} + {{- if not .Values.existingClickhouse.secret }} + username: {{ .Values.existingClickhouse.username }} + {{- else }} + username: $CLICKHOUSE_USERNAME + {{- end }} + secureJsonData: + {{- if not .Values.existingClickhouse.secret }} + password: {{ .Values.existingClickhouse.password }} + {{- else }} + password: $CLICKHOUSE_PASSWORD + {{- end }} + {{- end }} + {{- end }} {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-vertamedia-datasource.yaml b/charts/client/templates/configmap-vertamedia-datasource.yaml index 3b2ce491..983a15ee 100644 --- a/charts/client/templates/configmap-vertamedia-datasource.yaml +++ b/charts/client/templates/configmap-vertamedia-datasource.yaml @@ -11,6 +11,24 @@ data: datasources: - name: vertamedia-clickhouse-datasource type: vertamedia-clickhouse-datasource + {{- if .Values.clickhouse.enabled }} url: {{ include "client.fullname" . }}-clickhouse:8123 + {{- else }} + url: {{ .Values.existingClickhouse.host }}:8123 access: proxy + {{- if not .Values.clickhouse.enabled }} + basicAuth: true + {{- if not .Values.existingClickhouse.secret }} + basicAuthUser: {{ .Values.existingClickhouse.username }} + {{- else }} + basicAuthUser: $CLICKHOUSE_USERNAME + {{- end }} + secureJsonData: + {{- if not .Values.existingClickhouse.secret }} + basicAuthPassword: {{ .Values.existingClickhouse.password }} + {{- else }} + basicAuthPassword: $CLICKHOUSE_PASSWORD + {{- end }} + {{- end }} + {{- end }} {{- end }} \ No newline at end of file diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index d33e17ea..5058a310 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -40,7 +40,29 @@ spec: - name: SCHEMA_PATH value : {{ .Values.migration.schema.path }} - name: DB_ADDRESS + {{- if .Values.clickhouse.enabled }} value: {{ include "client.fullname" . }}-clickhouse + {{- else }} + value: {{ .Values.existingClickhouse.host }} + - name: CLICKHOUSE_USERNAME + {{- if not .Values.existingClickhouse.secret }} + value: {{ .Values.existingClickhouse.username }} + {{- else }} + valueFrom: + secretKeyRef: + name: {{ .Values.existingClickhouse.secret.name }} + key: {{ .Values.existingClickhouse.secret.usernamekey }} + {{- end }} + - name: CLICKHOUSE_PASSWORD + {{- if not .Values.existingClickhouse.secret }} + value: {{ .Values.existingClickhouse.password }} + {{- else }} + valueFrom: + secretKeyRef: + name: {{ .Values.existingClickhouse.secret.name }} + key: {{ .Values.existingClickhouse.secret.passwordkey }} + {{- end }} + {{- end }} - name: DB_PORT value: "9000" containers: @@ -74,7 +96,29 @@ spec: - name: NATS_ADDRESS value: {{ include "client.fullname" . }}-nats - name: DB_ADDRESS + {{- if .Values.clickhouse.enabled }} value: {{ include "client.fullname" . }}-clickhouse + {{- else }} + value: {{ .Values.existingClickhouse.host }} + - name: CLICKHOUSE_USERNAME + {{- if not .Values.existingClickhouse.secret }} + value: {{ .Values.existingClickhouse.username }} + {{- else }} + valueFrom: + secretKeyRef: + name: {{ .Values.existingClickhouse.secret.name }} + key: {{ .Values.existingClickhouse.secret.usernamekey }} + {{- end }} + - name: CLICKHOUSE_PASSWORD + {{- if not .Values.existingClickhouse.secret }} + value: {{ .Values.existingClickhouse.password }} + {{- else }} + valueFrom: + secretKeyRef: + name: {{ .Values.existingClickhouse.secret.name }} + key: {{ .Values.existingClickhouse.secret.passwordkey }} + {{- end }} + {{- end }} - name: DB_PORT value: "9000" resources: diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 6534c258..39acbc2d 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -98,6 +98,17 @@ clickhouse: clickhouse: replicas: "1" +existingClickhouse: + host: clickhouse + # Use username and password if you want to provide the token via Helm Values + username: "" + password: "" + # Use a secret reference if you want to get a username and password from a secret + secret: {} + # name: "" + # usernamekey: "" + # passwordkey: "" + grafana: enabled: false plugins: @@ -122,6 +133,14 @@ grafana: mountPath: /etc/secrets/postgresql secretName: kubviz-client-postgresql readOnly: true + clickhouse: + enabled: false + username: "" + password: "" + existingSecret: {} + # name: "" + # usernamekey: "" + # passwordkey: "" dashboards: enabled: true From fc1e6de19c801ad1c846d6b5814306fc56a1125f Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 8 Nov 2023 20:50:41 +0530 Subject: [PATCH 121/263] Add ttl Env's --- charts/client/templates/deployment.yaml | 8 ++++++++ charts/client/values.yaml | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index 5058a310..bce647bd 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -65,6 +65,10 @@ spec: {{- end }} - name: DB_PORT value: "9000" + - name: TTL_INTERVAL + value: "{{ .Values.ttl.ttlInterval }}" + - name: TTL_UNIT + value: {{ .Values.ttl.ttlUnit }} containers: - name: {{ .Chart.Name }} securityContext: @@ -121,6 +125,10 @@ spec: {{- end }} - name: DB_PORT value: "9000" + - name: TTL_INTERVAL + value: "{{ .Values.ttl.ttlInterval }}" + - name: TTL_UNIT + value: {{ .Values.ttl.ttlUnit }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 39acbc2d..3028aae9 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -161,3 +161,7 @@ migration: tag: "v1.1.3" schema: path: "/sql" + +ttl: + ttlInterval: "1" + ttlUnit: MONTH From 71fa9847bbfab581baeb197ebf2aa1b4eb5f9646 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Thu, 9 Nov 2023 17:46:08 +0530 Subject: [PATCH 122/263] Dashboard modifications --- charts/client/Chart.yaml | 2 +- .../templates/configmap-azure-dashboard.yaml | 31 +- .../configmap-bitbucket-dashboard.yaml | 16 +- .../templates/configmap-gitea-dashboard.yaml | 19 +- .../templates/configmap-github-dashboard.yaml | 22 +- .../templates/configmap-gitlab-dashboard.yaml | 36 +- .../configmap-kubedata-dashboard.yaml | 254 ++++++++- .../templates/configmap-kubviz-dashboard.yaml | 505 ++++++++++++++--- grafana/azure-dashboard.json | 31 +- grafana/bitBucket-dashboard.json | 16 +- grafana/giTea-dashboard.json | 19 +- grafana/gitHub-dashboard.json | 22 +- grafana/gitLab-dashboard.json | 36 +- grafana/kubeData-dashboard.json | 254 ++++++++- grafana/kubvizDsahboard.json | 507 +++++++++++++++--- 15 files changed, 1452 insertions(+), 318 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index a7d900bb..c2a5d128 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.8 +version: 1.1.9 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-azure-dashboard.yaml b/charts/client/templates/configmap-azure-dashboard.yaml index f0bfca39..ea290c78 100644 --- a/charts/client/templates/configmap-azure-dashboard.yaml +++ b/charts/client/templates/configmap-azure-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 12, + "id": 33, "links": [], "liveNow": false, "panels": [ @@ -55,7 +55,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -114,7 +114,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,7 +173,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -223,8 +223,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -294,8 +293,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -372,8 +370,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -429,11 +426,7 @@ data: "templating": { "list": [ { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, + "current": {}, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" @@ -452,11 +445,7 @@ data: "type": "query" }, { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, + "current": {}, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" @@ -484,7 +473,7 @@ data: "timezone": "", "title": "Azure", "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-bitbucket-dashboard.yaml b/charts/client/templates/configmap-bitbucket-dashboard.yaml index 04a61d88..fde60b9f 100644 --- a/charts/client/templates/configmap-bitbucket-dashboard.yaml +++ b/charts/client/templates/configmap-bitbucket-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 13, + "id": 34, "links": [], "liveNow": false, "panels": [ @@ -55,7 +55,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -114,7 +114,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,7 +173,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -223,8 +223,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -294,8 +293,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -483,7 +481,7 @@ data: "timezone": "", "title": "BitBucket", "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-gitea-dashboard.yaml b/charts/client/templates/configmap-gitea-dashboard.yaml index db0eca73..3c1a663f 100644 --- a/charts/client/templates/configmap-gitea-dashboard.yaml +++ b/charts/client/templates/configmap-gitea-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 11, + "id": 35, "links": [], "liveNow": false, "panels": [ @@ -55,7 +55,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -114,7 +114,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,7 +173,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -223,8 +223,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -294,8 +293,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -372,8 +370,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -484,7 +481,7 @@ data: "timezone": "", "title": "GiTea", "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-github-dashboard.yaml b/charts/client/templates/configmap-github-dashboard.yaml index 9373cd00..860f77a1 100644 --- a/charts/client/templates/configmap-github-dashboard.yaml +++ b/charts/client/templates/configmap-github-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 10, + "id": 37, "links": [], "liveNow": false, "panels": [ @@ -55,7 +55,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", "google": { "callback": "gmapReady", "key": "" @@ -115,7 +115,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n\n", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -174,7 +174,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -407,8 +407,8 @@ data: { "current": { "selected": false, - "text": "All", - "value": "$__all" + "text": "", + "value": "" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -429,9 +429,13 @@ data: }, { "current": { - "selected": false, - "text": "All", - "value": "$__all" + "selected": true, + "text": [ + "push" + ], + "value": [ + "push" + ] }, "datasource": { "type": "vertamedia-clickhouse-datasource", diff --git a/charts/client/templates/configmap-gitlab-dashboard.yaml b/charts/client/templates/configmap-gitlab-dashboard.yaml index 382d3e04..bc1273b7 100644 --- a/charts/client/templates/configmap-gitlab-dashboard.yaml +++ b/charts/client/templates/configmap-gitlab-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 14, + "id": 36, "links": [], "liveNow": false, "panels": [ @@ -55,7 +55,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -114,7 +114,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,7 +173,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -223,8 +223,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -294,8 +293,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -433,13 +431,9 @@ data: "list": [ { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "selected": false, + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -460,13 +454,9 @@ data: }, { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "selected": false, + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -495,7 +485,7 @@ data: "timezone": "", "title": "GitLab", "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubedata-dashboard.yaml b/charts/client/templates/configmap-kubedata-dashboard.yaml index ff4b9e7c..9a263900 100644 --- a/charts/client/templates/configmap-kubedata-dashboard.yaml +++ b/charts/client/templates/configmap-kubedata-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 9, + "id": 31, "links": [], "liveNow": false, "panels": [ @@ -42,11 +42,247 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { - "h": 11, + "h": 14, "w": 24, "x": 0, "y": 0 }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Kind IN ('Pod', 'Node', 'Deployment')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "rawQuery": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE EventTime >= toDateTime(1698927439) AND EventTime <= toDateTime(1698927739) AND ClusterName IN ('beta-cluster') AND Kind IN ('Pod', 'Node', 'Deployment')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "K8s metrics status", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 8, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Kind IN ('Service')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "rawQuery": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE EventTime >= toDateTime(1698931712) AND EventTime <= toDateTime(1698932012) AND ClusterName IN ('beta-cluster') AND Kind IN ('Service')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Service Events by Cluster and Reason", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 9, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);\n\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('PersistentVolume','PersistentVolumeClaim')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC\n", + "rawQuery": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE EventTime >= toDateTime(1698931738) AND EventTime <= toDateTime(1698932038) AND Kind IN ('PersistentVolume','PersistentVolumeClaim')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "PV, PVC events by cluster and reason", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 13, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 7, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Cluster Hierarchy',\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n const option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Start directly from the Cluster Hierarchy node\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Host, Reason, EventTime\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Kind = 'Node' AND Kind != ' '\nGROUP BY ClusterName, Host, Reason, EventTime", + "rawQuery": "SELECT ClusterName, Host, Reason, EventTime\nFROM default.events\nWHERE EventTime >= toDateTime(1698927382) AND EventTime <= toDateTime(1698927682) AND ClusterName IN ('beta-cluster') AND Kind = 'Node' AND Kind != ' '\nGROUP BY ClusterName, Host, Reason, EventTime", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Node Events by Cluster, Host, Reason, and EventTime", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 35 + }, "id": 5, "options": { "baidu": { @@ -61,7 +297,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract data from your JSON\nconst clusterNames = data.series[0].fields[0].values; // New column for ClusterName\nconst namespaces = data.series[0].fields[1].values;\nconst kinds = data.series[0].fields[2].values; // Adjusted index for Kind\nconst counts = data.series[0].fields[3].values; // New column for Count\n\n// Create a hierarchical structure from the data without a root node\nconst hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n};\n\nconst seenClusterNames = new Set();\nconst seenNamespaces = new Set();\n\nfor (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n}\n\n// Create the tree chart using ECharts\nconst option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n};\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values; // New column for ClusterName\n const namespaces = data.series[0].fields[1].values;\n const kinds = data.series[0].fields[2].values; // Adjusted index for Kind\n const counts = data.series[0].fields[3].values; // New column for Count\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -112,8 +348,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -128,7 +363,7 @@ data: "h": 7, "w": 24, "x": 0, - "y": 11 + "y": 46 }, "id": 4, "options": { @@ -191,8 +426,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -207,7 +441,7 @@ data: "h": 16, "w": 24, "x": 0, - "y": 18 + "y": 53 }, "id": 2, "options": { @@ -353,7 +587,7 @@ data: "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 2, + "version": 1, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubviz-dashboard.yaml b/charts/client/templates/configmap-kubviz-dashboard.yaml index c79ea4bb..6ee2c56c 100644 --- a/charts/client/templates/configmap-kubviz-dashboard.yaml +++ b/charts/client/templates/configmap-kubviz-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 126, + "id": 32, "links": [], "liveNow": false, "panels": [ @@ -184,6 +184,381 @@ data: "title": "Clusters with Most Activity", "type": "gauge" }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 9, + "w": 13, + "x": 0, + "y": 6 + }, + "id": 72, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const reasons = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const eventTimes = data.series[0].fields[2].values;\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n reasons.forEach((reason, index) => {\n const sourceNode = {\n name: reason,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const kindNode = {\n name: kinds[index],\n category: 1, // Category for kind nodes\n symbolSize: 40, // Size for kind nodes\n };\n\n const eventTimeNode = {\n name: eventTimes[index],\n category: 2, // Category for eventTime nodes\n symbolSize: 20, // Size for eventTime nodes\n };\n\n // Ensure source, kind, and eventTime nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === kindNode.name)) {\n nodes.push(kindNode);\n }\n\n if (!nodes.some((node) => node.name === eventTimeNode.name)) {\n nodes.push(eventTimeNode);\n }\n\n // Create links between reason, kind, and eventTime nodes\n links.push({\n source: reason,\n target: kinds[index],\n });\n\n links.push({\n source: kinds[index],\n target: eventTimes[index],\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Reasons',\n },\n {\n name: 'Nodes',\n },\n {\n name: 'Event Times',\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Reasons', 'Nodes', 'Event Times'],\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: {\n color: '#000',\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n edgeSymbolSize: [12, 12],\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Reason, Host, EventTime\nFROM default.events\nWHERE Kind IN ('Node') AND Reason IN ('NodeNotReady')", + "rawQuery": "SELECT Reason, Host, EventTime\nFROM default.events\nWHERE Kind IN ('Node') AND Reason IN ('NodeNotReady')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "NodeNotReady Events for Nodes", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 13, + "y": 6 + }, + "id": 70, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let kind = [];\nlet resources = [];\n\ndata.series.map((s) => {\n kind = s.fields.find((f) => f.name === 'Kind').values;\n resources = s.fields.find((f) => f.name === 'Resources').values;\n});\n\n// Create an empty array to store doughnut chart data\nconst doughnutChartData = [];\n\n// Define colors for doughnut slices\nconst doughnutSliceColors = ['#235894', '#FFFF00', '#FF0000', '#00FF00', '#FFA500'];\n\n// Map severity and counts to doughnut chart data\nkind.forEach((kinddata, index) => {\n doughnutChartData.push({\n value: resources[index],\n name: kinddata,\n itemStyle: {\n borderRadius: [10, 10, 10, 10], // Add rounded corners\n color: doughnutSliceColors[index % doughnutSliceColors.length],\n borderWidth: 2,\n borderColor: '#fff',\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {\n trigger: 'item',\n },\n legend: {\n top: '5%',\n left: 'center',\n },\n series: [\n {\n name: 'Access From',\n type: 'pie',\n radius: ['40%', '70%'],\n avoidLabelOverlap: false,\n label: {\n show: false,\n position: 'center',\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 40,\n fontWeight: 'bold',\n },\n },\n labelLine: {\n show: false,\n },\n data: doughnutChartData, // Use the modified doughnut chart data\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, count(Resource) AS Resources\nFROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime) AND Kind In ('Deployment','Job','Secret', 'ConfigMap', 'Service')\nGROUP BY ClusterName, Kind", + "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources\nFROM default.getall_resources\nWHERE EventTime >= toDateTime(1698841487) AND EventTime <= toDateTime(1698927887) AND Kind In ('Deployment','Job','Secret', 'ConfigMap', 'Service')\nGROUP BY ClusterName, Kind", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Resource Distribution", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 30, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 69, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": " SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'Created'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'Created'\n GROUP BY EventTime, Kind, Reason", + "refId": "A", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": " SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'BackOff'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'BackOff'\n GROUP BY EventTime, Kind, Reason", + "refId": "B", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'NodeNotReady'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'NodeNotReady'\n GROUP BY EventTime, Kind, Reason", + "refId": "C", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": " SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'Scheduled'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'Scheduled'\n GROUP BY EventTime, Kind, Reason", + "refId": "D", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'FailedScheduling'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'FailedScheduling'\n GROUP BY EventTime, Kind, Reason", + "refId": "E", + "round": "0s", + "skip_comments": true + } + ], + "title": "Pod Events by Reason Over Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Age" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 71, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Resource, Age\nFROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT ClusterName, Resource, Age\nFROM default.getall_resources\nWHERE EventTime >= toDateTime(1696336208) AND EventTime <= toDateTime(1698928208)", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Resource Details by Cluster and Age", + "type": "table" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -217,7 +592,7 @@ data: "h": 4, "w": 5, "x": 0, - "y": 6 + "y": 22 }, "id": 18, "options": { @@ -287,7 +662,7 @@ data: "h": 4, "w": 5, "x": 5, - "y": 6 + "y": 22 }, "id": 20, "options": { @@ -357,7 +732,7 @@ data: "h": 4, "w": 5, "x": 10, - "y": 6 + "y": 22 }, "id": 22, "options": { @@ -427,7 +802,7 @@ data: "h": 4, "w": 5, "x": 15, - "y": 6 + "y": 22 }, "id": 24, "options": { @@ -497,7 +872,7 @@ data: "h": 4, "w": 4, "x": 20, - "y": 6 + "y": 22 }, "id": 26, "options": { @@ -600,7 +975,7 @@ data: "h": 6, "w": 13, "x": 0, - "y": 10 + "y": 26 }, "id": 53, "options": { @@ -701,7 +1076,7 @@ data: "h": 6, "w": 11, "x": 13, - "y": 10 + "y": 26 }, "id": 65, "options": { @@ -757,8 +1132,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -773,7 +1147,7 @@ data: "h": 4, "w": 6, "x": 0, - "y": 16 + "y": 32 }, "id": 57, "options": { @@ -826,8 +1200,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -846,7 +1219,7 @@ data: "h": 4, "w": 6, "x": 6, - "y": 16 + "y": 32 }, "id": 63, "options": { @@ -897,8 +1270,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -917,7 +1289,7 @@ data: "h": 4, "w": 6, "x": 12, - "y": 16 + "y": 32 }, "id": 61, "options": { @@ -968,8 +1340,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -988,7 +1359,7 @@ data: "h": 4, "w": 6, "x": 18, - "y": 16 + "y": 32 }, "id": 62, "options": { @@ -1043,8 +1414,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1059,7 +1429,7 @@ data: "h": 4, "w": 6, "x": 0, - "y": 20 + "y": 36 }, "id": 56, "options": { @@ -1112,8 +1482,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1132,7 +1501,7 @@ data: "h": 4, "w": 6, "x": 6, - "y": 20 + "y": 36 }, "id": 58, "options": { @@ -1183,8 +1552,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1203,7 +1571,7 @@ data: "h": 4, "w": 6, "x": 12, - "y": 20 + "y": 36 }, "id": 59, "options": { @@ -1254,8 +1622,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1274,7 +1641,7 @@ data: "h": 4, "w": 6, "x": 18, - "y": 20 + "y": 36 }, "id": 60, "options": { @@ -1336,8 +1703,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "light-yellow", @@ -1381,7 +1747,7 @@ data: "h": 8, "w": 24, "x": 0, - "y": 24 + "y": 40 }, "id": 44, "options": { @@ -1435,8 +1801,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1455,7 +1820,7 @@ data: "h": 5, "w": 8, "x": 0, - "y": 32 + "y": 48 }, "id": 66, "options": { @@ -1505,8 +1870,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1525,7 +1889,7 @@ data: "h": 5, "w": 8, "x": 8, - "y": 32 + "y": 48 }, "id": 68, "options": { @@ -1575,8 +1939,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1595,7 +1958,7 @@ data: "h": 5, "w": 8, "x": 16, - "y": 32 + "y": 48 }, "id": 67, "options": { @@ -1657,8 +2020,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1690,7 +2052,7 @@ data: "h": 8, "w": 24, "x": 0, - "y": 37 + "y": 53 }, "id": 64, "options": { @@ -1766,8 +2128,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1782,7 +2143,7 @@ data: "h": 9, "w": 12, "x": 0, - "y": 45 + "y": 61 }, "id": 34, "options": { @@ -1866,8 +2227,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1882,7 +2242,7 @@ data: "h": 9, "w": 12, "x": 12, - "y": 45 + "y": 61 }, "id": 36, "options": { @@ -1966,8 +2326,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1982,7 +2341,7 @@ data: "h": 8, "w": 8, "x": 0, - "y": 54 + "y": 70 }, "id": 28, "options": { @@ -2065,8 +2424,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2081,7 +2439,7 @@ data: "h": 8, "w": 8, "x": 8, - "y": 54 + "y": 70 }, "id": 32, "options": { @@ -2165,8 +2523,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2181,7 +2538,7 @@ data: "h": 8, "w": 8, "x": 16, - "y": 54 + "y": 70 }, "id": 30, "options": { @@ -2251,8 +2608,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "light-yellow", @@ -2288,7 +2644,7 @@ data: "h": 7, "w": 24, "x": 0, - "y": 62 + "y": 78 }, "id": 42, "options": { @@ -2335,7 +2691,7 @@ data: "h": 1, "w": 24, "x": 0, - "y": 69 + "y": 85 }, "id": 16, "panels": [ @@ -2363,8 +2719,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2379,7 +2734,7 @@ data: "h": 16, "w": 24, "x": 0, - "y": 70 + "y": 110 }, "id": 14, "options": { @@ -2440,7 +2795,7 @@ data: "h": 1, "w": 24, "x": 0, - "y": 70 + "y": 86 }, "id": 12, "panels": [ @@ -2482,7 +2837,7 @@ data: "h": 16, "w": 24, "x": 0, - "y": 87 + "y": 127 }, "id": 10, "options": { @@ -2543,7 +2898,7 @@ data: "h": 1, "w": 24, "x": 0, - "y": 71 + "y": 87 }, "id": 8, "panels": [ @@ -2586,7 +2941,7 @@ data: "h": 11, "w": 24, "x": 0, - "y": 72 + "y": 112 }, "id": 6, "options": { @@ -2647,7 +3002,7 @@ data: "h": 1, "w": 24, "x": 0, - "y": 72 + "y": 88 }, "id": 4, "panels": [ @@ -2690,7 +3045,7 @@ data: "h": 8, "w": 24, "x": 0, - "y": 73 + "y": 113 }, "id": 2, "options": { diff --git a/grafana/azure-dashboard.json b/grafana/azure-dashboard.json index a634cc28..bea62afc 100644 --- a/grafana/azure-dashboard.json +++ b/grafana/azure-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 12, + "id": 33, "links": [], "liveNow": false, "panels": [ @@ -44,7 +44,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -212,8 +212,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -283,8 +282,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -361,8 +359,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -418,11 +415,7 @@ "templating": { "list": [ { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, + "current": {}, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" @@ -441,11 +434,7 @@ "type": "query" }, { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, + "current": {}, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" @@ -473,6 +462,6 @@ "timezone": "", "title": "Azure", "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", - "version": 2, + "version": 1, "weekStart": "" } \ No newline at end of file diff --git a/grafana/bitBucket-dashboard.json b/grafana/bitBucket-dashboard.json index 172b9d0b..389c001c 100644 --- a/grafana/bitBucket-dashboard.json +++ b/grafana/bitBucket-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 13, + "id": 34, "links": [], "liveNow": false, "panels": [ @@ -44,7 +44,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -212,8 +212,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -283,8 +282,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -472,6 +470,6 @@ "timezone": "", "title": "BitBucket", "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", - "version": 2, + "version": 1, "weekStart": "" } \ No newline at end of file diff --git a/grafana/giTea-dashboard.json b/grafana/giTea-dashboard.json index 5f21b3c7..da0e63a2 100644 --- a/grafana/giTea-dashboard.json +++ b/grafana/giTea-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 11, + "id": 35, "links": [], "liveNow": false, "panels": [ @@ -44,7 +44,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -212,8 +212,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -283,8 +282,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -361,8 +359,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -473,6 +470,6 @@ "timezone": "", "title": "GiTea", "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", - "version": 2, + "version": 1, "weekStart": "" } \ No newline at end of file diff --git a/grafana/gitHub-dashboard.json b/grafana/gitHub-dashboard.json index 71420e64..8b3930db 100644 --- a/grafana/gitHub-dashboard.json +++ b/grafana/gitHub-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 10, + "id": 37, "links": [], "liveNow": false, "panels": [ @@ -44,7 +44,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", "google": { "callback": "gmapReady", "key": "" @@ -104,7 +104,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n\n", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -163,7 +163,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -396,8 +396,8 @@ { "current": { "selected": false, - "text": "All", - "value": "$__all" + "text": "", + "value": "" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -418,9 +418,13 @@ }, { "current": { - "selected": false, - "text": "All", - "value": "$__all" + "selected": true, + "text": [ + "push" + ], + "value": [ + "push" + ] }, "datasource": { "type": "vertamedia-clickhouse-datasource", diff --git a/grafana/gitLab-dashboard.json b/grafana/gitLab-dashboard.json index feb82973..00c49d0b 100644 --- a/grafana/gitLab-dashboard.json +++ b/grafana/gitLab-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 14, + "id": 36, "links": [], "liveNow": false, "panels": [ @@ -44,7 +44,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'force',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [10, 10], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}\n", + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -103,7 +103,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Push_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%', // Adjust the left margin to position it on the top left\n top: '5%', // Adjust the top margin to position it on the top left\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n name: 'Push_Events',\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -162,7 +162,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract Author and Merge_Events data from the series\nconst categories = data.series[0].fields.find((f) => f.name === 'Author').values;\nconst mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n// Create the ECharts options\nconst options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n};\n\nreturn options;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -212,8 +212,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -283,8 +282,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -422,13 +420,9 @@ "list": [ { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "selected": false, + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -449,13 +443,9 @@ }, { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "selected": false, + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -484,6 +474,6 @@ "timezone": "", "title": "GitLab", "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", - "version": 2, + "version": 1, "weekStart": "" } \ No newline at end of file diff --git a/grafana/kubeData-dashboard.json b/grafana/kubeData-dashboard.json index ce8dfaf6..e253f234 100644 --- a/grafana/kubeData-dashboard.json +++ b/grafana/kubeData-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 9, + "id": 31, "links": [], "liveNow": false, "panels": [ @@ -31,11 +31,247 @@ "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { - "h": 11, + "h": 14, "w": 24, "x": 0, "y": 0 }, + "id": 6, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Kind IN ('Pod', 'Node', 'Deployment')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "rawQuery": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE EventTime >= toDateTime(1698927439) AND EventTime <= toDateTime(1698927739) AND ClusterName IN ('beta-cluster') AND Kind IN ('Pod', 'Node', 'Deployment')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "K8s metrics status", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 14 + }, + "id": 8, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Kind IN ('Service')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "rawQuery": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE EventTime >= toDateTime(1698931712) AND EventTime <= toDateTime(1698932012) AND ClusterName IN ('beta-cluster') AND Kind IN ('Service')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Service Events by Cluster and Reason", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 14 + }, + "id": 9, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);\n\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('PersistentVolume','PersistentVolumeClaim')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC\n", + "rawQuery": "SELECT ClusterName, Kind, Reason, COUNT(*) as Count\nFROM default.events\nWHERE EventTime >= toDateTime(1698931738) AND EventTime <= toDateTime(1698932038) AND Kind IN ('PersistentVolume','PersistentVolumeClaim')\nGROUP BY ClusterName, Kind, Reason\nORDER BY Count DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "PV, PVC events by cluster and reason", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 13, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 7, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Cluster Hierarchy',\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n const option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Start directly from the Cluster Hierarchy node\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Host, Reason, EventTime\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND ClusterName IN ($clusterName) AND Kind = 'Node' AND Kind != ' '\nGROUP BY ClusterName, Host, Reason, EventTime", + "rawQuery": "SELECT ClusterName, Host, Reason, EventTime\nFROM default.events\nWHERE EventTime >= toDateTime(1698927382) AND EventTime <= toDateTime(1698927682) AND ClusterName IN ('beta-cluster') AND Kind = 'Node' AND Kind != ' '\nGROUP BY ClusterName, Host, Reason, EventTime", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Node Events by Cluster, Host, Reason, and EventTime", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 35 + }, "id": 5, "options": { "baidu": { @@ -50,7 +286,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Extract data from your JSON\nconst clusterNames = data.series[0].fields[0].values; // New column for ClusterName\nconst namespaces = data.series[0].fields[1].values;\nconst kinds = data.series[0].fields[2].values; // Adjusted index for Kind\nconst counts = data.series[0].fields[3].values; // New column for Count\n\n// Create a hierarchical structure from the data without a root node\nconst hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n};\n\nconst seenClusterNames = new Set();\nconst seenNamespaces = new Set();\n\nfor (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n}\n\n// Create the tree chart using ECharts\nconst option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n};\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values; // New column for ClusterName\n const namespaces = data.series[0].fields[1].values;\n const kinds = data.series[0].fields[2].values; // Adjusted index for Kind\n const counts = data.series[0].fields[3].values; // New column for Count\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -101,8 +337,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -117,7 +352,7 @@ "h": 7, "w": 24, "x": 0, - "y": 11 + "y": 46 }, "id": 4, "options": { @@ -180,8 +415,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -196,7 +430,7 @@ "h": 16, "w": 24, "x": 0, - "y": 18 + "y": 53 }, "id": 2, "options": { @@ -342,6 +576,6 @@ "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 2, + "version": 1, "weekStart": "" } \ No newline at end of file diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index e4968f20..374d131b 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 126, + "id": 32, "links": [], "liveNow": false, "panels": [ @@ -173,6 +173,381 @@ "title": "Clusters with Most Activity", "type": "gauge" }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 9, + "w": 13, + "x": 0, + "y": 6 + }, + "id": 72, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const reasons = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const eventTimes = data.series[0].fields[2].values;\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n reasons.forEach((reason, index) => {\n const sourceNode = {\n name: reason,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const kindNode = {\n name: kinds[index],\n category: 1, // Category for kind nodes\n symbolSize: 40, // Size for kind nodes\n };\n\n const eventTimeNode = {\n name: eventTimes[index],\n category: 2, // Category for eventTime nodes\n symbolSize: 20, // Size for eventTime nodes\n };\n\n // Ensure source, kind, and eventTime nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === kindNode.name)) {\n nodes.push(kindNode);\n }\n\n if (!nodes.some((node) => node.name === eventTimeNode.name)) {\n nodes.push(eventTimeNode);\n }\n\n // Create links between reason, kind, and eventTime nodes\n links.push({\n source: reason,\n target: kinds[index],\n });\n\n links.push({\n source: kinds[index],\n target: eventTimes[index],\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Reasons',\n },\n {\n name: 'Nodes',\n },\n {\n name: 'Event Times',\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Reasons', 'Nodes', 'Event Times'],\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: {\n color: '#000',\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n edgeSymbolSize: [12, 12],\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Reason, Host, EventTime\nFROM default.events\nWHERE Kind IN ('Node') AND Reason IN ('NodeNotReady')", + "rawQuery": "SELECT Reason, Host, EventTime\nFROM default.events\nWHERE Kind IN ('Node') AND Reason IN ('NodeNotReady')", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "NodeNotReady Events for Nodes", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 13, + "y": 6 + }, + "id": 70, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let kind = [];\nlet resources = [];\n\ndata.series.map((s) => {\n kind = s.fields.find((f) => f.name === 'Kind').values;\n resources = s.fields.find((f) => f.name === 'Resources').values;\n});\n\n// Create an empty array to store doughnut chart data\nconst doughnutChartData = [];\n\n// Define colors for doughnut slices\nconst doughnutSliceColors = ['#235894', '#FFFF00', '#FF0000', '#00FF00', '#FFA500'];\n\n// Map severity and counts to doughnut chart data\nkind.forEach((kinddata, index) => {\n doughnutChartData.push({\n value: resources[index],\n name: kinddata,\n itemStyle: {\n borderRadius: [10, 10, 10, 10], // Add rounded corners\n color: doughnutSliceColors[index % doughnutSliceColors.length],\n borderWidth: 2,\n borderColor: '#fff',\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {\n trigger: 'item',\n },\n legend: {\n top: '5%',\n left: 'center',\n },\n series: [\n {\n name: 'Access From',\n type: 'pie',\n radius: ['40%', '70%'],\n avoidLabelOverlap: false,\n label: {\n show: false,\n position: 'center',\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 40,\n fontWeight: 'bold',\n },\n },\n labelLine: {\n show: false,\n },\n data: doughnutChartData, // Use the modified doughnut chart data\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Kind, count(Resource) AS Resources\nFROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime) AND Kind In ('Deployment','Job','Secret', 'ConfigMap', 'Service')\nGROUP BY ClusterName, Kind", + "rawQuery": "SELECT ClusterName, Kind, count(Resource) AS Resources\nFROM default.getall_resources\nWHERE EventTime >= toDateTime(1698841487) AND EventTime <= toDateTime(1698927887) AND Kind In ('Deployment','Job','Secret', 'ConfigMap', 'Service')\nGROUP BY ClusterName, Kind", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Resource Distribution", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 30, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 69, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": " SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'Created'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'Created'\n GROUP BY EventTime, Kind, Reason", + "refId": "A", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": " SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'BackOff'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'BackOff'\n GROUP BY EventTime, Kind, Reason", + "refId": "B", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'NodeNotReady'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'NodeNotReady'\n GROUP BY EventTime, Kind, Reason", + "refId": "C", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": " SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'Scheduled'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'Scheduled'\n GROUP BY EventTime, Kind, Reason", + "refId": "D", + "round": "0s", + "skip_comments": true + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "time_series", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "hide": false, + "intervalFactor": 1, + "query": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE $timeFilterByColumn(EventTime) AND Kind = 'Pod' AND Reason = 'FailedScheduling'\n GROUP BY EventTime, Kind, Reason", + "rawQuery": "SELECT EventTime, Kind, Reason, count(*) as total_count\n FROM default.events\n WHERE EventTime >= toDateTime(1698839664) AND EventTime <= toDateTime(1698926064) AND Kind = 'Pod' AND Reason = 'FailedScheduling'\n GROUP BY EventTime, Kind, Reason", + "refId": "E", + "round": "0s", + "skip_comments": true + } + ], + "title": "Pod Events by Reason Over Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Age" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 15 + }, + "id": 71, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Resource, Age\nFROM default.getall_resources\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT ClusterName, Resource, Age\nFROM default.getall_resources\nWHERE EventTime >= toDateTime(1696336208) AND EventTime <= toDateTime(1698928208)", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Resource Details by Cluster and Age", + "type": "table" + }, { "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -206,7 +581,7 @@ "h": 4, "w": 5, "x": 0, - "y": 6 + "y": 22 }, "id": 18, "options": { @@ -276,7 +651,7 @@ "h": 4, "w": 5, "x": 5, - "y": 6 + "y": 22 }, "id": 20, "options": { @@ -346,7 +721,7 @@ "h": 4, "w": 5, "x": 10, - "y": 6 + "y": 22 }, "id": 22, "options": { @@ -416,7 +791,7 @@ "h": 4, "w": 5, "x": 15, - "y": 6 + "y": 22 }, "id": 24, "options": { @@ -486,7 +861,7 @@ "h": 4, "w": 4, "x": 20, - "y": 6 + "y": 22 }, "id": 26, "options": { @@ -589,7 +964,7 @@ "h": 6, "w": 13, "x": 0, - "y": 10 + "y": 26 }, "id": 53, "options": { @@ -690,7 +1065,7 @@ "h": 6, "w": 11, "x": 13, - "y": 10 + "y": 26 }, "id": 65, "options": { @@ -746,8 +1121,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -762,7 +1136,7 @@ "h": 4, "w": 6, "x": 0, - "y": 16 + "y": 32 }, "id": 57, "options": { @@ -815,8 +1189,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -835,7 +1208,7 @@ "h": 4, "w": 6, "x": 6, - "y": 16 + "y": 32 }, "id": 63, "options": { @@ -886,8 +1259,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -906,7 +1278,7 @@ "h": 4, "w": 6, "x": 12, - "y": 16 + "y": 32 }, "id": 61, "options": { @@ -957,8 +1329,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -977,7 +1348,7 @@ "h": 4, "w": 6, "x": 18, - "y": 16 + "y": 32 }, "id": 62, "options": { @@ -1032,8 +1403,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1048,7 +1418,7 @@ "h": 4, "w": 6, "x": 0, - "y": 20 + "y": 36 }, "id": 56, "options": { @@ -1101,8 +1471,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1121,7 +1490,7 @@ "h": 4, "w": 6, "x": 6, - "y": 20 + "y": 36 }, "id": 58, "options": { @@ -1172,8 +1541,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1192,7 +1560,7 @@ "h": 4, "w": 6, "x": 12, - "y": 20 + "y": 36 }, "id": 59, "options": { @@ -1243,8 +1611,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1263,7 +1630,7 @@ "h": 4, "w": 6, "x": 18, - "y": 20 + "y": 36 }, "id": 60, "options": { @@ -1325,8 +1692,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "light-yellow", @@ -1370,7 +1736,7 @@ "h": 8, "w": 24, "x": 0, - "y": 24 + "y": 40 }, "id": 44, "options": { @@ -1424,8 +1790,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1444,7 +1809,7 @@ "h": 5, "w": 8, "x": 0, - "y": 32 + "y": 48 }, "id": 66, "options": { @@ -1494,8 +1859,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1514,7 +1878,7 @@ "h": 5, "w": 8, "x": 8, - "y": 32 + "y": 48 }, "id": 68, "options": { @@ -1564,8 +1928,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1584,7 +1947,7 @@ "h": 5, "w": 8, "x": 16, - "y": 32 + "y": 48 }, "id": 67, "options": { @@ -1646,8 +2009,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1679,7 +2041,7 @@ "h": 8, "w": 24, "x": 0, - "y": 37 + "y": 53 }, "id": 64, "options": { @@ -1755,8 +2117,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1771,7 +2132,7 @@ "h": 9, "w": 12, "x": 0, - "y": 45 + "y": 61 }, "id": 34, "options": { @@ -1855,8 +2216,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1871,7 +2231,7 @@ "h": 9, "w": 12, "x": 12, - "y": 45 + "y": 61 }, "id": 36, "options": { @@ -1955,8 +2315,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1971,7 +2330,7 @@ "h": 8, "w": 8, "x": 0, - "y": 54 + "y": 70 }, "id": 28, "options": { @@ -2054,8 +2413,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2070,7 +2428,7 @@ "h": 8, "w": 8, "x": 8, - "y": 54 + "y": 70 }, "id": 32, "options": { @@ -2154,8 +2512,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2170,7 +2527,7 @@ "h": 8, "w": 8, "x": 16, - "y": 54 + "y": 70 }, "id": 30, "options": { @@ -2240,8 +2597,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "light-yellow", @@ -2277,7 +2633,7 @@ "h": 7, "w": 24, "x": 0, - "y": 62 + "y": 78 }, "id": 42, "options": { @@ -2324,7 +2680,7 @@ "h": 1, "w": 24, "x": 0, - "y": 69 + "y": 85 }, "id": 16, "panels": [ @@ -2352,8 +2708,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2368,7 +2723,7 @@ "h": 16, "w": 24, "x": 0, - "y": 70 + "y": 110 }, "id": 14, "options": { @@ -2429,7 +2784,7 @@ "h": 1, "w": 24, "x": 0, - "y": 70 + "y": 86 }, "id": 12, "panels": [ @@ -2471,7 +2826,7 @@ "h": 16, "w": 24, "x": 0, - "y": 87 + "y": 127 }, "id": 10, "options": { @@ -2532,7 +2887,7 @@ "h": 1, "w": 24, "x": 0, - "y": 71 + "y": 87 }, "id": 8, "panels": [ @@ -2575,7 +2930,7 @@ "h": 11, "w": 24, "x": 0, - "y": 72 + "y": 112 }, "id": 6, "options": { @@ -2636,7 +2991,7 @@ "h": 1, "w": 24, "x": 0, - "y": 72 + "y": 88 }, "id": 4, "panels": [ @@ -2679,7 +3034,7 @@ "h": 8, "w": 24, "x": 0, - "y": 73 + "y": 113 }, "id": 2, "options": { @@ -2739,7 +3094,7 @@ "list": [] }, "time": { - "from": "now-5m", + "from": "now-24h", "to": "now" }, "timepicker": {}, From b2d2c21412f6cde9f4ff2d063346bf106cdc732b Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 10 Nov 2023 11:39:21 +0530 Subject: [PATCH 123/263] minor cache clean up and small lint changes --- agent/kubviz/kubePreUpgrade.go | 18 +++++++++++++----- agent/kubviz/outdated.go | 34 ++++++++++++++++------------------ agent/kubviz/trivy.go | 1 + agent/kubviz/trivy_image.go | 11 +++++++++++ agent/kubviz/trivy_sbom.go | 1 + 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index 2c49997e..ee3a6806 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -57,7 +57,6 @@ var result *model.Result func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamContext) error { for _, deprecatedAPI := range result.DeprecatedAPIs { deprecatedAPI.ClusterName = ClusterName - fmt.Println("deprecatedAPI", deprecatedAPI) deprecatedAPIJson, _ := json.Marshal(deprecatedAPI) _, err := js.Publish(constants.EventSubject_depricated, deprecatedAPIJson) if err != nil { @@ -90,7 +89,7 @@ func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error if err != nil { return err } - defer os.RemoveAll(filename) + defer os.RemoveAll(swaggerdir) swaggerfile := filename kubernetesAPIs, err := PopulateKubeAPIMap(swaggerfile) if err != nil { @@ -178,23 +177,32 @@ func getKubeAPIValues(value map[string]interface{}) (model.KubeAPI, bool) { return model.KubeAPI{}, false } func downloadFile(filename, url string) error { - log.Debugf("Downloading file from %s", url) resp, err := http.Get(url) if err != nil { log.Error(err) + return err } if resp.StatusCode > 305 { log.Errorf("could not download the swagger file %s", url) + return fmt.Errorf("failed to download file, status code: %d", resp.StatusCode) } + contentLength := resp.ContentLength + log.Infof("The size of the file to be downloaded for kubepreupgrade plugin is %d bytes", contentLength) + defer resp.Body.Close() out, err := os.Create(filename) if err != nil { log.Error(err) } defer out.Close() - _, err = io.Copy(out, resp.Body) + bytesCopied, err := io.Copy(out, resp.Body) + if err != nil { + log.WithError(err).Error("Failed to copy the file contents") + return err + } + log.Infof("Downloaded %d bytes for file %s", bytesCopied, filename) - return err + return nil } func getGroupVersionKind(value map[string]interface{}) (group, version, kind string) { diff --git a/agent/kubviz/outdated.go b/agent/kubviz/outdated.go index 4470abe8..ce3a77d3 100644 --- a/agent/kubviz/outdated.go +++ b/agent/kubviz/outdated.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/intelops/kubviz/constants" "log" "os" "regexp" @@ -15,10 +14,12 @@ import ( "sync" "time" + "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" - "github.com/docker/docker/api/types" + types "github.com/docker/docker/api/types/registry" "github.com/genuinetools/reg/registry" semver "github.com/hashicorp/go-version" "github.com/pkg/errors" @@ -137,7 +138,7 @@ func ParseImageName(imageName string) (string, string, string, error) { matches := dockerImageNameRegex.FindStringSubmatch(imageName) if len(matches) != 5 { - return "", "", "", fmt.Errorf("Expected 5 matches in regex, but found %d", len(matches)) + return "", "", "", fmt.Errorf("expected 5 matches in regex, but found %d", len(matches)) } hostname := matches[1] @@ -184,9 +185,7 @@ func ListImages(config *rest.Config) ([]model.RunningImage, error) { for _, pod := range pods.Items { for _, initContainerStatus := range pod.Status.InitContainerStatuses { pullable := initContainerStatus.ImageID - if strings.HasPrefix(pullable, "docker-pullable://") { - pullable = strings.TrimPrefix(pullable, "docker-pullable://") - } + pullable = strings.TrimPrefix(pullable, "docker-pullable://") runningImage := model.RunningImage{ Pod: pod.Name, Namespace: pod.Namespace, @@ -199,9 +198,8 @@ func ListImages(config *rest.Config) ([]model.RunningImage, error) { for _, containerStatus := range pod.Status.ContainerStatuses { pullable := containerStatus.ImageID - if strings.HasPrefix(pullable, "docker-pullable://") { - pullable = strings.TrimPrefix(pullable, "docker-pullable://") - } + pullable = strings.TrimPrefix(pullable, "docker-pullable://") + runningImage := model.RunningImage{ Pod: pod.Name, Namespace: pod.Namespace, @@ -393,8 +391,8 @@ func fetchTags(reg *registry.Registry, imageName string) ([]string, error) { } func parseTags(tags []string) ([]*semver.Version, []string, error) { - semverTags := make([]*semver.Version, 0, 0) - nonSemverTags := make([]string, 0, 0) + semverTags := make([]*semver.Version, 0) + nonSemverTags := make([]string, 0) for _, tag := range tags { v, err := semver.NewVersion(tag) @@ -449,12 +447,12 @@ func splitOutlierSemvers(allSemverTags []*semver.Version) ([]*semver.Version, [] return outliers, remaining, nil } -func homeDir() string { - if h := os.Getenv("HOME"); h != "" { - return h - } - return os.Getenv("USERPROFILE") -} +// func homeDir() string { +// if h := os.Getenv("HOME"); h != "" { +// return h +// } +// return os.Getenv("USERPROFILE") +// } type VersionTag struct { Sort int `json:"sort"` @@ -547,7 +545,7 @@ func (c SemverTagCollection) Unique() ([]*semver.Version, error) { } } - result := make([]*semver.Version, 0, 0) + result := make([]*semver.Version, 0) for _, u := range unique { result = append(result, u) } diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 55fab4e0..a7033664 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -63,6 +63,7 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { if err != nil { return err } + cleanupCache("/tmp/.cache") return nil } diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index 48081e38..dfdb308d 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "log" + "os" "strings" "github.com/aquasecurity/trivy/pkg/types" @@ -47,6 +48,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { if err != nil { return err } + cleanupCache("/tmp/.cache") } return nil } @@ -65,3 +67,12 @@ func publishImageScanReports(report types.Report, js nats.JetStreamContext) erro log.Printf("Trivy image report with ID:%s has been published\n", metrics.ID) return nil } + +func cleanupCache(cacheDir string) { + err := os.RemoveAll(cacheDir) + if err != nil { + log.Printf("Failed to clean up cache directory %s: %v", cacheDir, err) + } else { + log.Printf("Cache directory %s cleaned up successfully", cacheDir) + } +} diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index fe9c5cf8..b4de9faf 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -78,6 +78,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { // Publish the report using the given function publishTrivySbomReport(report, js) + cleanupCache("/tmp/.cache") } return nil } From 60b0c95ea91604bec354367c4a076f6f12083d12 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 10 Nov 2023 22:35:52 +0530 Subject: [PATCH 124/263] conflict --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc832512..bb19e359 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ helm upgrade -i kubviz-agent kubviz/agent -n kubviz --set nats.host= Date: Fri, 10 Nov 2023 23:03:14 +0530 Subject: [PATCH 125/263] Add ephemeral-storage request and limit --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 4ef1b0e0..8ef7e797 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.5 +version: 1.1.6 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 2643bf52..c786a2b1 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -43,7 +43,7 @@ service: port: 80 git_bridge: - enabled: false + enabled: true image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always @@ -77,7 +77,7 @@ git_bridge: container_bridge: - enabled: false + enabled: true image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always @@ -125,9 +125,11 @@ resources: limits: cpu: 2 memory: 2Gi + ephemeral-storage: 1Gi requests: cpu: 200m memory: 256Mi + ephemeral-storage: 256Mi autoscaling: enabled: false From 9604ee320d09801e1e921816ea81cd86bd989773 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Fri, 10 Nov 2023 23:06:03 +0530 Subject: [PATCH 126/263] Add ephemeral-storage request and limit --- charts/agent/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index c786a2b1..f9e9c4d9 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -43,7 +43,7 @@ service: port: 80 git_bridge: - enabled: true + enabled: false image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always @@ -77,7 +77,7 @@ git_bridge: container_bridge: - enabled: true + enabled: false image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always From b0ce778ecffe5613b6855139bb42ccd932d60aab Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sun, 12 Nov 2023 14:16:51 +0530 Subject: [PATCH 127/263] Add ephemeral-storage limit --- charts/agent/values.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index f9e9c4d9..1d7024b1 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -129,8 +129,7 @@ resources: requests: cpu: 200m memory: 256Mi - ephemeral-storage: 256Mi - + autoscaling: enabled: false minReplicas: 1 From 83e7c680343bb8ea8cb37c614b1bc34baddfbefd Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Mon, 13 Nov 2023 16:11:15 +0530 Subject: [PATCH 128/263] fix --- sql/0000015_trivysbom.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index ece47761..163478cd 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS trivysbom ( package_url String, bom_ref String, serial_number String, - version INTEGER + version INTEGER, bom_format String, component_version String, component_mime_type String, From 40cb646b59e77dbb5bd829832c004e8b61baa092 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Mon, 13 Nov 2023 20:14:24 +0530 Subject: [PATCH 129/263] Postgres data persistence --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index bb19e359..f9ce3923 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,12 @@ helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$to **NOTE:** - If you want to enable Grafana with the client deployment, add `--set grafana.enabled=true` to the helm upgrade command. +- Kubviz provides a setup for Grafana with Postgres data persistence, ensuring that even if the grafana pod/service goes down, the data will persist, safeguarding crucial information for visualization and analysis. + +```bash +helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$token" --set grafana.enabled=true --set grafana.postgresql=true +``` + - If grafana already exist use the same upgrade command without --set grafana.enabled=true flag. ```bash @@ -102,6 +108,7 @@ helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$to Parameter | Description | Default --------- | ----------- | ------- `grafana.enabled` | If true, create grafana | `false` +`grafana.postgresql` | If true, create postgresql | `false` - The KubViz client will also install NATS and Clickhouse. The NATS service is exposed as a LoadBalancer, and you need to note the external IP of the service **kubviz-client-nats-external** and pass it during the KubViz agent installation. From e56439177375c28a9c4fd30da2678d6daa70542d Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 14 Nov 2023 11:39:17 +0530 Subject: [PATCH 130/263] added cache clearing command for trivy image, trivy sbom, and trivy k8s --- agent/kubviz/trivy.go | 16 +++++++--------- agent/kubviz/trivy_image.go | 11 +++++++++-- agent/kubviz/trivy_sbom.go | 9 +++++++-- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index a7033664..aaf71bbb 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -31,18 +31,12 @@ func executeCommandTrivy(command string) ([]byte, error) { func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { var report report.ConsolidatedReport cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir /tmp/.cache --debug" - - // Log the command before execution - log.Printf("Executing command: %s\n", cmdString) - - // Execute the command + clearCacheCmd := "trivy k8s --clear-cache" out, err := executeCommandTrivy(cmdString) - - // Handle errors and process the command output as needed if err != nil { log.Printf("Error executing command: %v\n", err) + return err } - // Log the command output for debugging purposes log.Printf("Command output: %s\n", out) outStr := string(out) parts := strings.SplitN(outStr, "{", 2) @@ -59,11 +53,15 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { log.Printf("Error occurred while Unmarshalling json for k8s cluster scan: %v", err) return err } + _, err = executeCommandTrivy(clearCacheCmd) + if err != nil { + log.Printf("Error executing command: %v\n", err) + return err + } err = publishTrivyK8sReport(report, js) if err != nil { return err } - cleanupCache("/tmp/.cache") return nil } diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index dfdb308d..bd50e642 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -15,9 +15,12 @@ import ( ) func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { + clearCacheCmd := "trivy image --clear-cache" + images, err := ListImages(config) if err != nil { - log.Fatal(err) + log.Println("error occured while trying to list images, error :", err.Error()) + return err } for _, image := range images { @@ -44,11 +47,15 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error occurred while Unmarshalling json for image: %v", err) continue // Move on to the next image in case of an error } + _, err = executeCommandTrivy(clearCacheCmd) + if err != nil { + log.Printf("Error executing command: %v\n", err) + return err + } err = publishImageScanReports(report, js) if err != nil { return err } - cleanupCache("/tmp/.cache") } return nil } diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index b4de9faf..8f87ef5b 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -46,6 +46,8 @@ func executeCommandSbom(command string) ([]byte, error) { } func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { + clearCacheCmd := "trivy image --clear-cache" + log.Println("trivy sbom run started") images, err := ListImages(config) @@ -75,10 +77,13 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image in case of an error } // log.Println("report", report) - + _, err = executeCommandTrivy(clearCacheCmd) + if err != nil { + log.Printf("Error executing command: %v\n", err) + return err + } // Publish the report using the given function publishTrivySbomReport(report, js) - cleanupCache("/tmp/.cache") } return nil } From ae786479bc6915f6f302553d29275f996939c692 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 14 Nov 2023 19:57:35 +0530 Subject: [PATCH 131/263] Add ephemeral-storage limit --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 26 ++++++++++++++------------ charts/client/Chart.yaml | 2 +- charts/client/values.yaml | 15 ++++++++------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 8ef7e797..9133af69 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.6 +version: 1.1.7 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 1d7024b1..7613d4ca 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -49,12 +49,13 @@ git_bridge: pullPolicy: Always tag: "v1.1.3" resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 200m - memory: 256Mi + limits: + cpu: 200m + memory: 256Mi + ephemeral-storage: 100Mi + requests: + cpu: 200m + memory: 256Mi ingress: enabled: true annotations: @@ -83,12 +84,13 @@ container_bridge: pullPolicy: Always tag: "v1.1.3" resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 200m - memory: 256Mi + limits: + cpu: 200m + memory: 256Mi + ephemeral-storage: 100Mi + requests: + cpu: 200m + memory: 256Mi ingress: enabled: true annotations: diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index c2a5d128..58ebc2dc 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.9 +version: 1.1.10 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 3028aae9..753e3c75 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -53,17 +53,18 @@ ingress: # hosts: # - chart-example.local -resources: {} +resources: # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi + limits: + cpu: 100m + memory: 128Mi + ephemeral-storage: 100Mi + requests: + cpu: 100m + memory: 128Mi autoscaling: enabled: false From 9813d5f2cb650bfa6f1f1a4e7a4fa8d0dbab61c6 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 15 Nov 2023 15:34:18 +0530 Subject: [PATCH 132/263] cpu and memory limit increased --- charts/client/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 753e3c75..f612cb05 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -59,8 +59,8 @@ resources: # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. limits: - cpu: 100m - memory: 128Mi + cpu: 200m + memory: 256Mi ephemeral-storage: 100Mi requests: cpu: 100m From 65a855ffedfb3c6a16de9464e5df32d40899ce17 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 20 Nov 2023 11:08:16 +0530 Subject: [PATCH 133/263] pvc modification done in KubePreUpgrade --- agent/kubviz/kubePreUpgrade.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index ee3a6806..590db681 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -79,19 +79,21 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon } func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error { - swaggerdir, err := os.MkdirTemp("", "kubepug") + pvcMountPath := "/mnt/agent/kbz" + uniqueDir := fmt.Sprintf("%s/kubepug", pvcMountPath) + err := os.Mkdir(uniqueDir, 0755) if err != nil { return err } - filename := fmt.Sprintf("%s/swagger-%s.json", swaggerdir, k8sVersion) + + filename := fmt.Sprintf("%s/swagger-%s.json", uniqueDir, k8sVersion) url := fmt.Sprintf("%s/%s/%s", baseURL, k8sVersion, fileURL) err = downloadFile(filename, url) if err != nil { return err } - defer os.RemoveAll(swaggerdir) - swaggerfile := filename - kubernetesAPIs, err := PopulateKubeAPIMap(swaggerfile) + defer os.RemoveAll(filename) + kubernetesAPIs, err := PopulateKubeAPIMap(filename) if err != nil { return err } From 002c868ec0d03d9e434a637f2e77f88ba7f12c2d Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 20 Nov 2023 11:33:36 +0530 Subject: [PATCH 134/263] added pvc in trivy image --- agent/kubviz/kubePreUpgrade.go | 1 - agent/kubviz/outdated.go | 7 ------- agent/kubviz/trivy_image.go | 13 ++++++++++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index 590db681..11ec310f 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -104,7 +104,6 @@ func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error func PopulateKubeAPIMap(swagfile string) (model.KubernetesAPIs, error) { var kubeAPIs = make(model.KubernetesAPIs) - // log.Infof("Populating the PopulateKubeAPIMap") jsonFile, err := os.Open(swagfile) if err != nil { log.Error(err) diff --git a/agent/kubviz/outdated.go b/agent/kubviz/outdated.go index ce3a77d3..6e0e7dcd 100644 --- a/agent/kubviz/outdated.go +++ b/agent/kubviz/outdated.go @@ -447,13 +447,6 @@ func splitOutlierSemvers(allSemverTags []*semver.Version) ([]*semver.Version, [] return outliers, remaining, nil } -// func homeDir() string { -// if h := os.Getenv("HOME"); h != "" { -// return h -// } -// return os.Getenv("USERPROFILE") -// } - type VersionTag struct { Sort int `json:"sort"` Version string `json:"version"` diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index bd50e642..9af06a97 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "log" "os" "strings" @@ -15,6 +16,15 @@ import ( ) func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { + + pvcMountPath := "/mnt/agent/kbz" + trivyImageCacheDir := fmt.Sprintf("%s/trivy-imagecache", pvcMountPath) + err := os.MkdirAll(trivyImageCacheDir, 0755) + if err != nil { + log.Printf("Error creating Trivy Image cache directory: %v\n", err) + return err + } + clearCacheCmd := "trivy image --clear-cache" images, err := ListImages(config) @@ -25,7 +35,8 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { for _, image := range images { var report types.Report - out, err := executeCommand("trivy image " + image.PullableImage + " --timeout 60m -f json -q --cache-dir /tmp/.cache") + scanCmd := fmt.Sprintf("trivy image %s --timeout 60m -f json -q --cache-dir %s", image.PullableImage, trivyImageCacheDir) + out, err := executeCommand(scanCmd) if err != nil { log.Printf("Error scanning image %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error From b2eb54a1c73bdd33fd8caca5b4406a379bf82a5a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 20 Nov 2023 11:38:14 +0530 Subject: [PATCH 135/263] added pvc in trivy sbom --- agent/kubviz/trivy_image.go | 9 --------- agent/kubviz/trivy_sbom.go | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index 9af06a97..5486f0ba 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -85,12 +85,3 @@ func publishImageScanReports(report types.Report, js nats.JetStreamContext) erro log.Printf("Trivy image report with ID:%s has been published\n", metrics.ID) return nil } - -func cleanupCache(cacheDir string) { - err := os.RemoveAll(cacheDir) - if err != nil { - log.Printf("Failed to clean up cache directory %s: %v", cacheDir, err) - } else { - log.Printf("Cache directory %s cleaned up successfully", cacheDir) - } -} diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 8f87ef5b..5e58bbb6 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log" + "os" "os/exec" "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" @@ -47,18 +48,20 @@ func executeCommandSbom(command string) ([]byte, error) { func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { clearCacheCmd := "trivy image --clear-cache" - - log.Println("trivy sbom run started") + pvcMountPath := "/mnt/agent/kbz" + trivySbomCacheDir := fmt.Sprintf("%s/trivy-sbomcache", pvcMountPath) + err := os.MkdirAll(trivySbomCacheDir, 0755) + if err != nil { + log.Printf("Error creating Trivy cache directory: %v\n", err) + return err + } images, err := ListImages(config) - if err != nil { log.Printf("failed to list images: %v", err) } for _, image := range images { - - command := fmt.Sprintf("trivy image --format cyclonedx %s %s", image.PullableImage, "--cache-dir /tmp/.cache") - out, err := executeCommandSbom(command) - + sbomcmd := fmt.Sprintf("trivy image --format cyclonedx %s --cache-dir %s", image.PullableImage, trivySbomCacheDir) + out, err := executeCommandSbom(sbomcmd) if err != nil { log.Printf("Error executing Trivy for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error @@ -69,7 +72,6 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Trivy output is empty for image sbom %s", image.PullableImage) continue // Move on to the next image } - var report cyclonedx.BOM err = json.Unmarshal(out, &report) if err != nil { From 0ae4bbadefaec240af76a623029db839e63084a8 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 20 Nov 2023 11:41:43 +0530 Subject: [PATCH 136/263] added pvc to trivy --- agent/kubviz/trivy.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index aaf71bbb..04ee6f81 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -3,7 +3,9 @@ package main import ( "bytes" "encoding/json" + "fmt" "log" + "os" exec "os/exec" "strings" @@ -29,8 +31,15 @@ func executeCommandTrivy(command string) ([]byte, error) { return outc.Bytes(), err } func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { + pvcMountPath := "/mnt/agent/kbz" + trivyCacheDir := fmt.Sprintf("%s/trivy-cache", pvcMountPath) + err := os.MkdirAll(trivyCacheDir, 0755) + if err != nil { + log.Printf("Error creating Trivy cache directory: %v\n", err) + return err + } var report report.ConsolidatedReport - cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir /tmp/.cache --debug" + cmdString := fmt.Sprintf("trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir %s --debug", trivyCacheDir) clearCacheCmd := "trivy k8s --clear-cache" out, err := executeCommandTrivy(cmdString) if err != nil { From ca6737c8a7b60f32d07c64c43896f6491d4a6756 Mon Sep 17 00:00:00 2001 From: vijeyash <91282703+vijeyash1@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:11:00 +0530 Subject: [PATCH 137/263] Revert "Pvc" --- agent/kubviz/kubePreUpgrade.go | 13 ++++++------- agent/kubviz/outdated.go | 7 +++++++ agent/kubviz/trivy.go | 11 +---------- agent/kubviz/trivy_image.go | 22 ++++++++++------------ agent/kubviz/trivy_sbom.go | 18 ++++++++---------- 5 files changed, 32 insertions(+), 39 deletions(-) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index 11ec310f..ee3a6806 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -79,21 +79,19 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon } func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error { - pvcMountPath := "/mnt/agent/kbz" - uniqueDir := fmt.Sprintf("%s/kubepug", pvcMountPath) - err := os.Mkdir(uniqueDir, 0755) + swaggerdir, err := os.MkdirTemp("", "kubepug") if err != nil { return err } - - filename := fmt.Sprintf("%s/swagger-%s.json", uniqueDir, k8sVersion) + filename := fmt.Sprintf("%s/swagger-%s.json", swaggerdir, k8sVersion) url := fmt.Sprintf("%s/%s/%s", baseURL, k8sVersion, fileURL) err = downloadFile(filename, url) if err != nil { return err } - defer os.RemoveAll(filename) - kubernetesAPIs, err := PopulateKubeAPIMap(filename) + defer os.RemoveAll(swaggerdir) + swaggerfile := filename + kubernetesAPIs, err := PopulateKubeAPIMap(swaggerfile) if err != nil { return err } @@ -104,6 +102,7 @@ func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error func PopulateKubeAPIMap(swagfile string) (model.KubernetesAPIs, error) { var kubeAPIs = make(model.KubernetesAPIs) + // log.Infof("Populating the PopulateKubeAPIMap") jsonFile, err := os.Open(swagfile) if err != nil { log.Error(err) diff --git a/agent/kubviz/outdated.go b/agent/kubviz/outdated.go index 6e0e7dcd..ce3a77d3 100644 --- a/agent/kubviz/outdated.go +++ b/agent/kubviz/outdated.go @@ -447,6 +447,13 @@ func splitOutlierSemvers(allSemverTags []*semver.Version) ([]*semver.Version, [] return outliers, remaining, nil } +// func homeDir() string { +// if h := os.Getenv("HOME"); h != "" { +// return h +// } +// return os.Getenv("USERPROFILE") +// } + type VersionTag struct { Sort int `json:"sort"` Version string `json:"version"` diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 04ee6f81..aaf71bbb 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -3,9 +3,7 @@ package main import ( "bytes" "encoding/json" - "fmt" "log" - "os" exec "os/exec" "strings" @@ -31,15 +29,8 @@ func executeCommandTrivy(command string) ([]byte, error) { return outc.Bytes(), err } func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { - pvcMountPath := "/mnt/agent/kbz" - trivyCacheDir := fmt.Sprintf("%s/trivy-cache", pvcMountPath) - err := os.MkdirAll(trivyCacheDir, 0755) - if err != nil { - log.Printf("Error creating Trivy cache directory: %v\n", err) - return err - } var report report.ConsolidatedReport - cmdString := fmt.Sprintf("trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir %s --debug", trivyCacheDir) + cmdString := "trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir /tmp/.cache --debug" clearCacheCmd := "trivy k8s --clear-cache" out, err := executeCommandTrivy(cmdString) if err != nil { diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index 5486f0ba..bd50e642 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "fmt" "log" "os" "strings" @@ -16,15 +15,6 @@ import ( ) func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { - - pvcMountPath := "/mnt/agent/kbz" - trivyImageCacheDir := fmt.Sprintf("%s/trivy-imagecache", pvcMountPath) - err := os.MkdirAll(trivyImageCacheDir, 0755) - if err != nil { - log.Printf("Error creating Trivy Image cache directory: %v\n", err) - return err - } - clearCacheCmd := "trivy image --clear-cache" images, err := ListImages(config) @@ -35,8 +25,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { for _, image := range images { var report types.Report - scanCmd := fmt.Sprintf("trivy image %s --timeout 60m -f json -q --cache-dir %s", image.PullableImage, trivyImageCacheDir) - out, err := executeCommand(scanCmd) + out, err := executeCommand("trivy image " + image.PullableImage + " --timeout 60m -f json -q --cache-dir /tmp/.cache") if err != nil { log.Printf("Error scanning image %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error @@ -85,3 +74,12 @@ func publishImageScanReports(report types.Report, js nats.JetStreamContext) erro log.Printf("Trivy image report with ID:%s has been published\n", metrics.ID) return nil } + +func cleanupCache(cacheDir string) { + err := os.RemoveAll(cacheDir) + if err != nil { + log.Printf("Failed to clean up cache directory %s: %v", cacheDir, err) + } else { + log.Printf("Cache directory %s cleaned up successfully", cacheDir) + } +} diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 5e58bbb6..8f87ef5b 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "log" - "os" "os/exec" "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" @@ -48,20 +47,18 @@ func executeCommandSbom(command string) ([]byte, error) { func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { clearCacheCmd := "trivy image --clear-cache" - pvcMountPath := "/mnt/agent/kbz" - trivySbomCacheDir := fmt.Sprintf("%s/trivy-sbomcache", pvcMountPath) - err := os.MkdirAll(trivySbomCacheDir, 0755) - if err != nil { - log.Printf("Error creating Trivy cache directory: %v\n", err) - return err - } + + log.Println("trivy sbom run started") images, err := ListImages(config) + if err != nil { log.Printf("failed to list images: %v", err) } for _, image := range images { - sbomcmd := fmt.Sprintf("trivy image --format cyclonedx %s --cache-dir %s", image.PullableImage, trivySbomCacheDir) - out, err := executeCommandSbom(sbomcmd) + + command := fmt.Sprintf("trivy image --format cyclonedx %s %s", image.PullableImage, "--cache-dir /tmp/.cache") + out, err := executeCommandSbom(command) + if err != nil { log.Printf("Error executing Trivy for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error @@ -72,6 +69,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Trivy output is empty for image sbom %s", image.PullableImage) continue // Move on to the next image } + var report cyclonedx.BOM err = json.Unmarshal(out, &report) if err != nil { From 0965859dec1e059f3e0c8d88c1c1ef8e742717f5 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 21 Nov 2023 15:33:22 +0530 Subject: [PATCH 138/263] pvc changes --- agent/kubviz/kubePreUpgrade.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index 11ec310f..20da065f 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -81,7 +81,7 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error { pvcMountPath := "/mnt/agent/kbz" uniqueDir := fmt.Sprintf("%s/kubepug", pvcMountPath) - err := os.Mkdir(uniqueDir, 0755) + err := os.MkdirAll(uniqueDir, 0755) if err != nil { return err } From 030407fd4362494319d87e2eaafc6b0791281e08 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 28 Nov 2023 16:42:09 +0530 Subject: [PATCH 139/263] changed image to get shell access --- dockerfiles/client/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dockerfiles/client/Dockerfile b/dockerfiles/client/Dockerfile index 31af58c8..fd9f5b18 100644 --- a/dockerfiles/client/Dockerfile +++ b/dockerfiles/client/Dockerfile @@ -11,7 +11,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o k8smetri # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot +FROM golang:alpine WORKDIR / COPY --from=builder /workspace/k8smetrics_client . USER 65532:65532 From 28da13c80b9d10bc3ccea9f9e2e8137f8d878ffc Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 30 Nov 2023 11:22:48 +0530 Subject: [PATCH 140/263] error-handle --- client/pkg/clickhouse/db_client.go | 198 ++++++++++++++++++++++++----- 1 file changed, 168 insertions(+), 30 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 7a9aaddb..dc4a5460 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -134,9 +134,19 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushEventPayload) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertAzureContainerPushEvent)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + + stmt, err := tx.Prepare(string(InsertAzureContainerPushEvent)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() currentTime := time.Now().UTC() @@ -177,9 +187,21 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertQuayContainerPushEvent)) + tx, err = c.conn.Begin() ) + + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + + stmt, err := tx.Prepare(string(InsertQuayContainerPushEvent)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + + defer stmt.Close() currentTime := time.Now().UTC() @@ -222,9 +244,19 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertJfrogContainerPushEvent)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + + stmt, err := tx.Prepare(string(InsertJfrogContainerPushEvent)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() currentTime := time.Now().UTC() @@ -267,9 +299,18 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertRakees)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertRakees)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() currentTime := time.Now().UTC() @@ -292,9 +333,18 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { func (c *DBClient) InsertKetallEvent(metrics model.Resource) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertKetall)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertKetall)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() currentTime := time.Now().UTC() @@ -316,9 +366,18 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertOutdated)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertOutdated)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() currentTime := time.Now().UTC() @@ -342,9 +401,18 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertDepricatedApi)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertDepricatedApi)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() deprecated := uint8(0) @@ -375,9 +443,18 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertDeletedApi)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertDeletedApi)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() deleted := uint8(0) if deletedAPI.Deleted { @@ -409,9 +486,18 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertKubvizEvent)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertKubvizEvent)) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() eventJson, _ := json.Marshal(metrics.Event) formattedFirstTimestamp := metrics.Event.FirstTimestamp.Time.UTC().Format("2006-01-02 15:04:05") @@ -471,9 +557,17 @@ func (c *DBClient) InsertContainerEvent(event string) { func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(InsertKubeScore) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(InsertKubeScore) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } defer stmt.Close() currentTime := time.Now().UTC() @@ -497,9 +591,17 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { for _, result := range finding.Results { for _, vulnerability := range result.Vulnerabilities { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(InsertTrivyVul) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(InsertTrivyVul) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } if _, err := stmt.Exec( metrics.ID, metrics.ClusterName, @@ -528,9 +630,18 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { for _, misconfiguration := range result.Misconfigurations { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(InsertTrivyMisconfig) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(InsertTrivyMisconfig) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + defer stmt.Close() currentTime := time.Now().UTC() @@ -567,9 +678,18 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { for _, result := range metrics.Report.Results { for _, vulnerability := range result.Vulnerabilities { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(InsertTrivyImage) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(InsertTrivyImage) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + if _, err := stmt.Exec( metrics.ID, metrics.ClusterName, @@ -606,9 +726,18 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { if result.CycloneDX != nil { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(InsertTrivySbom) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(InsertTrivySbom) + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } + if _,err:= stmt.Exec( metrics.ID, result.CycloneDX.Metadata.Component.Name, @@ -733,9 +862,18 @@ func (c *DBClient) RetrieveKubvizEvent() ([]model.DbEvent, error) { func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { var ( - tx, _ = c.conn.Begin() - stmt, _ = tx.Prepare(string(InsertDockerHubBuild)) + tx, err = c.conn.Begin() ) + if err != nil { + log.Printf("error beginning transaction: %v", err) + return + } + stmt, err := tx.Prepare(string(InsertDockerHubBuild)) + + if err != nil { + log.Printf("error preparing statement: %v", err) + return + } defer stmt.Close() currentTime := time.Now().UTC() From 59cc0772c727067acc46c159c583418453b77346 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 30 Nov 2023 13:09:05 +0530 Subject: [PATCH 141/263] var keyword removed --- client/pkg/clickhouse/db_client.go | 105 ++++++++++------------------- 1 file changed, 37 insertions(+), 68 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index dc4a5460..011fc0ff 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -133,9 +133,8 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { } func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushEventPayload) { - var ( - tx, err = c.conn.Begin() - ) + + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -143,8 +142,8 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE stmt, err := tx.Prepare(string(InsertAzureContainerPushEvent)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Printf("error preparing statement: %v", err) + return } defer stmt.Close() @@ -186,10 +185,7 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE } func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { - var ( - tx, err = c.conn.Begin() - ) - + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -199,8 +195,7 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload if err != nil { log.Printf("error preparing statement: %v", err) return - } - + } defer stmt.Close() @@ -243,9 +238,7 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload } func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -255,7 +248,7 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() @@ -298,9 +291,7 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE } func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -309,7 +300,7 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() @@ -332,9 +323,7 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { } func (c *DBClient) InsertKetallEvent(metrics model.Resource) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -343,7 +332,7 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() @@ -365,9 +354,7 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { } func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -376,7 +363,7 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() @@ -400,9 +387,7 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { } func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -411,7 +396,7 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() @@ -442,9 +427,7 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { } func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -453,7 +436,7 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() deleted := uint8(0) @@ -485,9 +468,7 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { } func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -496,7 +477,7 @@ func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() eventJson, _ := json.Marshal(metrics.Event) @@ -556,9 +537,7 @@ func (c *DBClient) InsertContainerEvent(event string) { } func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -567,7 +546,7 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() currentTime := time.Now().UTC() @@ -590,9 +569,7 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { for _, finding := range metrics.Report.Findings { for _, result := range finding.Results { for _, vulnerability := range result.Vulnerabilities { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -601,7 +578,7 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } if _, err := stmt.Exec( metrics.ID, metrics.ClusterName, @@ -629,9 +606,7 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { } for _, misconfiguration := range result.Misconfigurations { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -640,7 +615,7 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() @@ -677,9 +652,7 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { for _, result := range metrics.Report.Results { for _, vulnerability := range result.Vulnerabilities { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -688,7 +661,7 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } if _, err := stmt.Exec( metrics.ID, @@ -725,9 +698,7 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { result := metrics.Report if result.CycloneDX != nil { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -736,9 +707,9 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } - if _,err:= stmt.Exec( + if _, err := stmt.Exec( metrics.ID, result.CycloneDX.Metadata.Component.Name, result.CycloneDX.Metadata.Component.PackageURL, @@ -748,18 +719,18 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { result.CycloneDX.BOMFormat, result.CycloneDX.Metadata.Component.Version, result.CycloneDX.Metadata.Component.MIMEType, - ); err!=nil { + ); err != nil { log.Fatal(err) } - if err:=tx.Commit();err!=nil { + if err := tx.Commit(); err != nil { log.Fatal(err) } stmt.Close() - }else { + } else { log.Println("sbom payload not available for db insertion, skipping db insertion") } - + } func (c *DBClient) Close() { _ = c.conn.Close() @@ -861,9 +832,7 @@ func (c *DBClient) RetrieveKubvizEvent() ([]model.DbEvent, error) { } func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { - var ( - tx, err = c.conn.Begin() - ) + tx, err := c.conn.Begin() if err != nil { log.Printf("error beginning transaction: %v", err) return @@ -873,7 +842,7 @@ func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { if err != nil { log.Printf("error preparing statement: %v", err) return - } + } defer stmt.Close() currentTime := time.Now().UTC() From 482c19c9c73bafc638861f666c8e5e49f9ee6cfe Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 4 Dec 2023 17:35:01 +0530 Subject: [PATCH 142/263] updated Clickhouse chart --- .github/workflows/helm_release.yml | 1 + charts/clickhouse/.helmignore | 0 charts/clickhouse/Chart.yaml | 29 +- charts/clickhouse/README.md | 652 ++++++-- charts/clickhouse/templates/NOTES.txt | 89 +- charts/clickhouse/templates/_helpers.tpl | 221 ++- .../templates/configmap-config.yaml | 112 -- .../clickhouse/templates/configmap-extra.yaml | 20 + .../templates/configmap-metrika.yaml | 77 - .../templates/configmap-users-extra.yaml | 20 + .../clickhouse/templates/configmap-users.yaml | 68 - charts/clickhouse/templates/configmap.yaml | 20 + .../templates/deployment-tabix.yaml | 85 - charts/clickhouse/templates/extra-list.yaml | 9 + .../templates/ingress-clickhouse.yaml | 27 - .../clickhouse/templates/ingress-tabix.yaml | 29 - .../templates/ingress-tls-secrets.yaml | 44 + charts/clickhouse/templates/ingress.yaml | 59 + .../templates/init-scripts-secret.yaml | 19 + .../clickhouse/templates/prometheusrule.yaml | 24 + .../templates/scripts-configmap.yaml | 34 + .../clickhouse/templates/service-account.yaml | 19 + .../templates/service-external-access.yaml | 155 ++ .../templates/service-headless.yaml | 69 + charts/clickhouse/templates/service.yaml | 152 ++ .../clickhouse/templates/servicemonitor.yaml | 47 + .../templates/start-scripts-secret.yaml | 19 + .../statefulset-clickhouse-replica.yaml | 184 --- .../templates/statefulset-clickhouse.yaml | 182 --- charts/clickhouse/templates/statefulset.yaml | 425 +++++ .../templates/svc-clickhouse-headless.yaml | 26 - .../svc-clickhouse-replica-headless.yaml | 26 - .../templates/svc-clickhouse-replica.yaml | 25 - .../clickhouse/templates/svc-clickhouse.yaml | 25 - charts/clickhouse/templates/svc-tabix.yaml | 19 - charts/clickhouse/templates/tls-secret.yaml | 29 + charts/clickhouse/values.yaml | 1455 +++++++++++++---- 37 files changed, 3045 insertions(+), 1451 deletions(-) mode change 100755 => 100644 charts/clickhouse/.helmignore mode change 100755 => 100644 charts/clickhouse/Chart.yaml mode change 100755 => 100644 charts/clickhouse/README.md mode change 100755 => 100644 charts/clickhouse/templates/NOTES.txt mode change 100755 => 100644 charts/clickhouse/templates/_helpers.tpl delete mode 100755 charts/clickhouse/templates/configmap-config.yaml create mode 100644 charts/clickhouse/templates/configmap-extra.yaml delete mode 100755 charts/clickhouse/templates/configmap-metrika.yaml create mode 100644 charts/clickhouse/templates/configmap-users-extra.yaml delete mode 100755 charts/clickhouse/templates/configmap-users.yaml create mode 100644 charts/clickhouse/templates/configmap.yaml delete mode 100755 charts/clickhouse/templates/deployment-tabix.yaml create mode 100644 charts/clickhouse/templates/extra-list.yaml delete mode 100755 charts/clickhouse/templates/ingress-clickhouse.yaml delete mode 100755 charts/clickhouse/templates/ingress-tabix.yaml create mode 100644 charts/clickhouse/templates/ingress-tls-secrets.yaml create mode 100644 charts/clickhouse/templates/ingress.yaml create mode 100644 charts/clickhouse/templates/init-scripts-secret.yaml create mode 100644 charts/clickhouse/templates/prometheusrule.yaml create mode 100644 charts/clickhouse/templates/scripts-configmap.yaml create mode 100644 charts/clickhouse/templates/service-account.yaml create mode 100644 charts/clickhouse/templates/service-external-access.yaml create mode 100644 charts/clickhouse/templates/service-headless.yaml create mode 100644 charts/clickhouse/templates/service.yaml create mode 100644 charts/clickhouse/templates/servicemonitor.yaml create mode 100644 charts/clickhouse/templates/start-scripts-secret.yaml delete mode 100755 charts/clickhouse/templates/statefulset-clickhouse-replica.yaml delete mode 100755 charts/clickhouse/templates/statefulset-clickhouse.yaml create mode 100644 charts/clickhouse/templates/statefulset.yaml delete mode 100755 charts/clickhouse/templates/svc-clickhouse-headless.yaml delete mode 100755 charts/clickhouse/templates/svc-clickhouse-replica-headless.yaml delete mode 100755 charts/clickhouse/templates/svc-clickhouse-replica.yaml delete mode 100755 charts/clickhouse/templates/svc-clickhouse.yaml delete mode 100755 charts/clickhouse/templates/svc-tabix.yaml create mode 100644 charts/clickhouse/templates/tls-secret.yaml mode change 100755 => 100644 charts/clickhouse/values.yaml diff --git a/.github/workflows/helm_release.yml b/.github/workflows/helm_release.yml index 597ddaf8..f16b4250 100644 --- a/.github/workflows/helm_release.yml +++ b/.github/workflows/helm_release.yml @@ -23,6 +23,7 @@ jobs: - name: Add Helm repos run: | helm repo add tools https://kube-tarian.github.io/helmrepo-supporting-tools + helm repo add bitnami https://charts.bitnami.com/bitnami - name: Run chart-releaser uses: helm/chart-releaser-action@v1.1.0 diff --git a/charts/clickhouse/.helmignore b/charts/clickhouse/.helmignore old mode 100755 new mode 100644 diff --git a/charts/clickhouse/Chart.yaml b/charts/clickhouse/Chart.yaml old mode 100755 new mode 100644 index e6ff5829..83ead92e --- a/charts/clickhouse/Chart.yaml +++ b/charts/clickhouse/Chart.yaml @@ -1,16 +1,23 @@ -appVersion: "19.14" -description: ClickHouse is an open source column-oriented database management system - capable of real time generation of analytical data reports using SQL queries -home: https://clickhouse.yandex/ -icon: https://clickhouse.yandex/images/logo.png +apiVersion: v2 +appVersion: 23.10.5 +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x +description: ClickHouse is an open-source column-oriented OLAP database management + system. Use it to boost your database performance while providing linear scalability + and hardware efficiency. +home: https://bitnami.com +icon: https://bitnami.com/assets/stacks/clickhouse/img/clickhouse-stack-220x234.png keywords: -- clickhouse -- olap - database +- sharding maintainers: -- email: 411934049@qq.com - name: liwenhe +- name: VMware, Inc. + url: https://github.com/bitnami/charts name: clickhouse sources: -- https://github.com/liwenhe1993/charts -version: 1.0.2 +- https://github.com/bitnami/charts/tree/main/bitnami/clickhouse +version: 1.0.3 diff --git a/charts/clickhouse/README.md b/charts/clickhouse/README.md old mode 100755 new mode 100644 index ec22a59f..ef654f7e --- a/charts/clickhouse/README.md +++ b/charts/clickhouse/README.md @@ -1,169 +1,529 @@ -# ClickHouse + -[ClickHouse](https://clickhouse.yandex/) is an open source column-oriented database management system capable of real time generation of analytical data reports using SQL queries. +# Bitnami package for ClickHouse + +ClickHouse is an open-source column-oriented OLAP database management system. Use it to boost your database performance while providing linear scalability and hardware efficiency. + +[Overview of ClickHouse](https://clickhouse.com/) + +Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + +## TL;DR + +```console +helm install my-release oci://registry-1.docker.io/bitnamicharts/clickhouse +``` + +Looking to use ClickHouse in production? Try [VMware Tanzu Application Catalog](https://bitnami.com/enterprise), the enterprise edition of Bitnami Application Catalog. ## Introduction -This chart bootstraps a [ClickHouse](https://clickhouse.yandex/) replication cluster deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts for Helm are carefully engineered, actively maintained and are the quickest and easiest way to deploy containers on a Kubernetes cluster that are ready to handle production workloads. + +This chart bootstraps a [ClickHouse](https://github.com/clickhouse/clickhouse) Deployment in a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +[Learn more about the default configuration of the chart](https://docs.bitnami.com/kubernetes/infrastructure/clickhouse/get-started/). ## Prerequisites -- Kubernetes 1.10+ +- Kubernetes 1.23+ +- Helm 3.8.0+ - PV provisioner support in the underlying infrastructure +- ReadWriteMany volumes for deployment scaling + +> If you are using Kubernetes 1.18, the following code needs to be commented out. +> seccompProfile: +> type: "RuntimeDefault" ## Installing the Chart To install the chart with the release name `my-release`: -```bash -$ helm repo add liwenhe https://liwenhe1993.github.io/charts/ -$ helm repo update -$ helm install --name clickhouse liwenhe/clickhouse +```console +helm install my-release oci://REGISTRY_NAME/REPOSITORY_NAME/clickhouse ``` -These commands deploy Clickhouse on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +The command deploys ClickHouse on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. > **Tip**: List all releases using `helm list` ## Uninstalling the Chart -To uninstall/delete the `clickhouse` deployment: +To uninstall/delete the `my-release` deployment: -```bash -$ helm delete --purge clickhouse +```console +helm delete my-release ``` The command removes all the Kubernetes components associated with the chart and deletes the release. -## Configuration - -The following tables lists the configurable parameters of the Clickhouse chart and their default values. - -| Parameter | Description | Default | -| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- | -| `timezone` | World time and date for cities in all time zones | `Asia/Shanghai` | -| `clusterDomain` | Kubernetes cluster domain | `cluster.local` | -| `affinity` | Clickhouse Node selectors and tolerations for pod assignment | `nil` | -| `clickhouse.podManagementPolicy` | StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees | `Parallel` | -| `clickhouse.updateStrategy` | StatefulSet controller supports automated updates. There are two valid update strategies: RollingUpdate and OnDelete | `RollingUpdate` | -| `clickhouse.rollingUpdatePartition` | Partition update strategy | `nil` | -| `clickhouse.path` | The path to the directory containing data | `/var/lib/clickhouse` | -| `clickhouse.http_port` | The port for connecting to the server over HTTP | `8123` | -| `clickhouse.tcp_port` | Port for communicating with clients over the TCP protocol | `9000` | -| `clickhouse.interserver_http_port` | Port for exchanging data between ClickHouse servers | `9009` | -| `clickhouse.replicas` | The instance number of Clickhouse | `3` | -| `clickhouse.image` | Docker image for Clickhouse | `yandex/clickhouse-server` | -| `clickhouse.imageVersion` | Docker image version for Clickhouse | `19.14` | -| `clickhouse.imagePullPolicy` | Image pull policy. One of Always, Never, IfNotPresent | `IfNotPresent` | -| `clickhouse.livenessProbe.enabled` | Turn on and off liveness probe | `true` | -| `clickhouse.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `30` | -| `clickhouse.livenessProbe.periodSeconds` | How often to perform the probe | `30` | -| `clickhouse.livenessProbe.timeoutSeconds` | When the probe times out | `5` | -| `clickhouse.livenessProbe.failureThreshold` | Minimum consecutive successes for the probe | `3` | -| `clickhouse.livenessProbe.successThreshold` | Minimum consecutive failures for the probe | `1` | -| `clickhouse.readinessProbe.enabled` | Turn on and off readiness probe | `true` | -| `clickhouse.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | `30` | -| `clickhouse.readinessProbe.periodSeconds` | How often to perform the probe | `30` | -| `clickhouse.readinessProbe.timeoutSeconds` | When the probe times out | `5` | -| `clickhouse.readinessProbe.failureThreshold` | Minimum consecutive successes for the probe | `3` | -| `clickhouse.readinessProbe.successThreshold` | Minimum consecutive failures for the probe | `1` | -| `clickhouse.persistentVolumeClaim.enabled` | Enable persistence using a `PersistentVolumeClaim` | `false` | -| `clickhouse.persistentVolumeClaim.dataPersistentVolume.enabled` | Turn on and off dataPersistentVolume | `false` | -| `clickhouse.persistentVolumeClaim.dataPersistentVolume.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` | -| `clickhouse.persistentVolumeClaim.dataPersistentVolume.storageClassName` | Persistent Volume Storage Class | `` | -| `clickhouse.persistentVolumeClaim.dataPersistentVolume.storage` | Persistent Volume Size | `500Gi` | -| `clickhouse.persistentVolumeClaim.logsPersistentVolume.enabled` | Turn on and off dataPersistentVolume | `false` | -| `clickhouse.persistentVolumeClaim.logsPersistentVolume.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` | -| `clickhouse.persistentVolumeClaim.logsPersistentVolume.storageClassName` | Persistent Volume Storage Class | `` | -| `clickhouse.persistentVolumeClaim.logsPersistentVolume.storage` | Persistent Volume Size | `50Gi` | -| `clickhouse.ingress.enabled` | Enable ingress | `false` | -| `clickhouse.ingress.host` | Ingress host | `` | -| `clickhouse.ingress.path` | Ingress path | `` | -| `clickhouse.ingress.tls.enabled` | Enable ingress tls | `false` | -| `clickhouse.ingress.tls.hosts` | Ingress tls hosts | `[]` | -| `clickhouse.ingress.tls.secretName` | Ingress tls `secretName` | `` | -| `clickhouse.configmap.enabled` | If Configmap's enabled is `true`, Custom `config.xml`, `metrica.xml` and `users.xml` | `true` | -| `clickhouse.configmap.max_connections` | The maximum number of inbound connections | `4096` | -| `clickhouse.configmap.keep_alive_timeout` | The number of seconds that ClickHouse waits for incoming requests before closing the connection | `3` | -| `clickhouse.configmap.max_concurrent_queries` | The maximum number of simultaneously processed requests | `100` | -| `clickhouse.configmap.uncompressed_cache_size` | Cache size (in bytes) for uncompressed data used by table engines from the MergeTree | `8589934592` | -| `clickhouse.configmap.mark_cache_size` | Approximate size (in bytes) of the cache of "marks" used by MergeTree | `5368709120` | -| `clickhouse.configmap.umask` | Number is always parsed as octal. Default umask is 027 (other users cannot read logs, data files, etc; group can only read) | `022` | -| `clickhouse.configmap.mlock_executable` | Enabling this option is recommended but will lead to increased startup time for up to a few seconds | `false` | -| `clickhouse.configmap.builtin_dictionaries_reload_interval` | The interval in seconds before reloading built-in dictionaries | `3600` | -| `clickhouse.configmap.max_session_timeout` | Maximum session timeout, in seconds | `3600` | -| `clickhouse.configmap.default_session_timeout` | Default session timeout, in seconds | `60` | -| `clickhouse.configmap.disable_internal_dns_cache` | Uncomment to disable ClickHouse internal DNS caching | `1` | -| `clickhouse.configmap.max_open_files` | The maximum number of open files | `` | -| `clickhouse.configmap.interserver_http_host` | The host name that can be used by other servers to access this server | `` | -| `clickhouse.configmap.logger.path` | The log file path | `/var/log/clickhouse-server` | -| `clickhouse.configmap.logger.level` | Logging level. Acceptable values: trace, debug, information, warning, error | `trace` | -| `clickhouse.configmap.logger.size` | Size of the file | `1000M` | -| `clickhouse.configmap.logger.count` | The number of archived log files that ClickHouse stores | `10` | -| `clickhouse.configmap.compression.enabled` | Enable data compression settings | `false` | -| `clickhouse.configmap.compression.cases[].min_part_size` | The minimum size of a table part | `10000000000` | -| `clickhouse.configmap.compression.cases[].min_part_size_ratio` | The ratio of the minimum size of a table part to the full size of the table | `0.01` | -| `clickhouse.configmap.compression.cases[].method` | Compression method. Acceptable values ​: lz4 or zstd(experimental) | `zstd` | -| `clickhouse.configmap.zookeeper_servers.enabled` | Enable contains settings that allow ClickHouse to interact with a ZooKeeper cluster | `false` | -| `clickhouse.configmap.zookeeper_servers.session_timeout_ms` | Maximum timeout for the client session in milliseconds | `30000` | -| `clickhouse.configmap.zookeeper_servers.operation_timeout_ms` | Operation timeout for the client session in milliseconds | `10000` | -| `clickhouse.configmap.zookeeper_servers.root` | The znode that is used as the root for znodes used by the ClickHouse server. Optional | `` | -| `clickhouse.configmap.zookeeper_servers.identity` | User and password, that can be required by ZooKeeper to give access to requested znodes. Optional | `` | -| `clickhouse.configmap.zookeeper_servers.config[].index` | ZooKeeper index | `` | -| `clickhouse.configmap.zookeeper_servers.config[].host` | ZooKeeper host | `` | -| `clickhouse.configmap.zookeeper_servers.config[].port` | ZooKeeper port | `` | -| `clickhouse.configmap.remote_servers.enabled` | Enable configuration of clusters used by the Distributed table engine | `true` | -| `clickhouse.configmap.remote_servers.internal_replication` | If this parameter is set to 'true', the table where data will be written is going to replicate them itself | `false` | -| `clickhouse.configmap.remote_servers.replica.user` | Name of the user for connecting to a remote server. Access is configured in the users.xml file. | `default` | -| `clickhouse.configmap.remote_servers.replica.password` | The password for connecting to a remote server (not masked). | `nil` | -| `clickhouse.configmap.remote_servers.replica.compression` | Use data compression. | `true` | -| `clickhouse.configmap.remote_servers.replica.backup.enabled` | Enable replica backup | `false` | -| `clickhouse.configmap.remote_servers.graphite.enabled` | Enable graphite | `false` | -| `clickhouse.configmap.remote_servers.graphite.config[].timeout` | The timeout for sending data, in seconds | `0.1` | -| `clickhouse.configmap.remote_servers.graphite.config[].interval` | The interval for sending, in seconds | `60` | -| `clickhouse.configmap.remote_servers.graphite.config[].root_path` | Prefix for keys | `one_min` | -| `clickhouse.configmap.remote_servers.graphite.config[].metrics` | Sending data from a :ref:system_tables-system.metrics table | `true` | -| `clickhouse.configmap.remote_servers.graphite.config[].events` | Sending deltas data accumulated for the time period from a :ref:system_tables-system.events table | `true` | -| `clickhouse.configmap.remote_servers.graphite.config[].events_cumulative` | Sending cumulative data from a :ref:system_tables-system.events table | `true` | -| `clickhouse.configmap.remote_servers.graphite.config[].asynchronous_metrics` | Sending data from a :ref:system_tables-system.asynchronous_metrics table | `true` | -| `clickhouse.configmap.profiles.enabled` | Enable a settings profiles | `false` | -| `clickhouse.configmap.profiles.profile[].name` | Tne name of a settings profile | `` | -| `clickhouse.configmap.profiles.profile[].config` | The config of a settings profile | `{}` | -| `clickhouse.configmap.users.enabled` | Enable a settings users | `false` | -| `clickhouse.configmap.users.user[].name` | Tne name of a settings user | `` | -| `clickhouse.configmap.users.user[].config` | Tne config of a settings user | `{}` | -| `clickhouse.configmap.quotas.enabled` | Enable a settings quotas | `false` | -| `clickhouse.configmap.quotas.quota[].name` | Tne name of a settings quota | `` | -| `clickhouse.configmap.quotas.quota[].config[]` | Tne config of a settings quota | `[]` | -| `tabix.enabled` | Enable tabix | `false` | -| `tabix.replicas` | The instance number of Tabix | `1` | -| `tabix.updateStrategy.type` | Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate | `RollingUpdate` | -| `tabix.updateStrategy.maxSurge` | The maximum number of pods that can be scheduled above the desired number of pods | `3` | -| `tabix.updateStrategy.maxUnavailable` | The maximum number of pods that can be unavailable during the update | `1` | -| `tabix.image` | Docker image name | `spoonest/clickhouse-tabix-web-client` | -| `tabix.imageVersion` | Docker image version | `stable` | -| `tabix.imagePullPolicy` | Dcoker image pull policy | `IfNotPresent` | -| `tabix.livenessProbe.enabled` | Turn on and off liveness probe | `true` | -| `tabix.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `30` | -| `tabix.livenessProbe.periodSeconds` | How often to perform the probe | `30` | -| `tabix.livenessProbe.timeoutSeconds` | When the probe times out | `5` | -| `tabix.livenessProbe.failureThreshold` | Minimum consecutive successes for the probe | `3` | -| `tabix.livenessProbe.successThreshold` | Minimum consecutive failures for the probe | `1` | -| `tabix.readinessProbe.enabled` | Turn on and off readiness probe | `true` | -| `tabix.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | `30` | -| `tabix.readinessProbe.periodSeconds` | How often to perform the probe | `30` | -| `tabix.readinessProbe.timeoutSeconds` | When the probe times out | `5` | -| `tabix.readinessProbe.failureThreshold` | Minimum consecutive successes for the probe | `3` | -| `tabix.readinessProbe.successThreshold` | Minimum consecutive failures for the probe | `1` | -| `tabix.security.user` | Tabix login username | `admin` | -| `tabix.security.password` | Tabix login password | `admin` | -| `tabix.automaticConnection.chName` | Automatic connection Clickhouse name | `` | -| `tabix.automaticConnection.chHost` | Automatic connection Clickhouse host | `` | -| `tabix.automaticConnection.chLogin` | Automatic connection Clickhouse login username | `` | -| `tabix.automaticConnection.chPassword` | Automatic connection Clickhouse login password | `` | -| `tabix.automaticConnection.chParams` | Automatic connection Clickhouse params | `` | -| `tabix.ingress.enabled` | Enable ingress | `false` | -| `tabix.ingress.host` | Ingress host | `` | -| `tabix.ingress.path` | Ingress path | `` | -| `tabix.ingress.tls.enabled` | Enable ingress tls | `false` | -| `tabix.ingress.tls.hosts` | Ingress tls hosts | `[]` | - -For more information please refer to the [liwenhe1993/charts](https://github.com/liwenhe1993/charts.git) documentation. +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------- | ----------------------------------------------- | ----- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | + +### Common parameters + +| Name | Description | Value | +| ------------------------ | --------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Override Kubernetes version | `""` | +| `nameOverride` | String to partially override common.names.name | `""` | +| `fullnameOverride` | String to fully override common.names.fullname | `""` | +| `namespaceOverride` | String to fully override common.names.namespace | `""` | +| `commonLabels` | Labels to add to all deployed objects | `{}` | +| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | +| `clusterDomain` | Kubernetes cluster domain name | `cluster.local` | +| `extraDeploy` | Array of extra objects to deploy with the release | `[]` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | + +### ClickHouse Parameters + +| Name | Description | Value | +| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------- | +| `image.registry` | ClickHouse image registry | `REGISTRY_NAME` | +| `image.repository` | ClickHouse image repository | `REPOSITORY_NAME/clickhouse` | +| `image.digest` | ClickHouse image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | ClickHouse image pull policy | `IfNotPresent` | +| `image.pullSecrets` | ClickHouse image pull secrets | `[]` | +| `image.debug` | Enable ClickHouse image debug mode | `false` | +| `shards` | Number of ClickHouse shards to deploy | `2` | +| `replicaCount` | Number of ClickHouse replicas per shard to deploy | `3` | +| `distributeReplicasByZone` | Schedules replicas of the same shard to different availability zones | `false` | +| `containerPorts.http` | ClickHouse HTTP container port | `8123` | +| `containerPorts.https` | ClickHouse HTTPS container port | `8443` | +| `containerPorts.tcp` | ClickHouse TCP container port | `9000` | +| `containerPorts.tcpSecure` | ClickHouse TCP (secure) container port | `9440` | +| `containerPorts.keeper` | ClickHouse keeper TCP container port | `2181` | +| `containerPorts.keeperSecure` | ClickHouse keeper TCP (secure) container port | `3181` | +| `containerPorts.keeperInter` | ClickHouse keeper interserver TCP container port | `9444` | +| `containerPorts.mysql` | ClickHouse MySQL container port | `9004` | +| `containerPorts.postgresql` | ClickHouse PostgreSQL container port | `9005` | +| `containerPorts.interserver` | ClickHouse Interserver container port | `9009` | +| `containerPorts.metrics` | ClickHouse metrics container port | `8001` | +| `livenessProbe.enabled` | Enable livenessProbe on ClickHouse containers | `true` | +| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `10` | +| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | +| `livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | +| `livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `readinessProbe.enabled` | Enable readinessProbe on ClickHouse containers | `true` | +| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `10` | +| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | +| `readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `startupProbe.enabled` | Enable startupProbe on ClickHouse containers | `false` | +| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `10` | +| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | +| `startupProbe.failureThreshold` | Failure threshold for startupProbe | `3` | +| `startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `resources.limits` | The resources limits for the ClickHouse containers | `{}` | +| `resources.requests` | The requested resources for the ClickHouse containers | `{}` | +| `podSecurityContext.enabled` | Enabled ClickHouse pods' Security Context | `true` | +| `podSecurityContext.fsGroup` | Set ClickHouse pod's Security Context fsGroup | `1001` | +| `containerSecurityContext.enabled` | Enable containers' Security Context | `true` | +| `containerSecurityContext.runAsUser` | Set containers' Security Context runAsUser | `1001` | +| `containerSecurityContext.runAsNonRoot` | Set containers' Security Context runAsNonRoot | `true` | +| `containerSecurityContext.readOnlyRootFilesystem` | Set read only root file system pod's | `false` | +| `containerSecurityContext.privileged` | Set contraller container's Security Context privileged | `false` | +| `containerSecurityContext.allowPrivilegeEscalation` | Set contraller container's Security Context allowPrivilegeEscalation | `false` | +| `containerSecurityContext.capabilities.drop` | List of capabilities to be droppedn | `["ALL"]` | +| `containerSecurityContext.seccompProfile.type` | Set container's Security Context seccomp profile | `RuntimeDefault` | +| `auth.username` | ClickHouse Admin username | `default` | +| `auth.password` | ClickHouse Admin password | `""` | +| `auth.existingSecret` | Name of a secret containing the Admin password | `""` | +| `auth.existingSecretKey` | Name of the key inside the existing secret | `""` | +| `logLevel` | Logging level | `information` | + +### ClickHouse keeper configuration parameters + +| Name | Description | Value | +| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------- | +| `keeper.enabled` | Deploy ClickHouse keeper. Support is experimental. | `false` | +| `defaultConfigurationOverrides` | Default configuration overrides (evaluated as a template) | `""` | +| `existingOverridesConfigmap` | The name of an existing ConfigMap with your custom configuration for ClickHouse | `""` | +| `extraOverrides` | Extra configuration overrides (evaluated as a template) apart from the default | `""` | +| `extraOverridesConfigmap` | The name of an existing ConfigMap with extra configuration for ClickHouse | `""` | +| `extraOverridesSecret` | The name of an existing ConfigMap with your custom configuration for ClickHouse | `""` | +| `usersExtraOverrides` | Users extra configuration overrides (evaluated as a template) apart from the default | `""` | +| `usersExtraOverridesConfigmap` | The name of an existing ConfigMap with users extra configuration for ClickHouse | `""` | +| `usersExtraOverridesSecret` | The name of an existing ConfigMap with your custom users configuration for ClickHouse | `""` | +| `initdbScripts` | Dictionary of initdb scripts | `{}` | +| `initdbScriptsSecret` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) | `""` | +| `startdbScripts` | Dictionary of startdb scripts | `{}` | +| `startdbScriptsSecret` | ConfigMap with the startdb scripts (Note: Overrides `startdbScripts`) | `""` | +| `command` | Override default container command (useful when using custom images) | `["/scripts/setup.sh"]` | +| `args` | Override default container args (useful when using custom images) | `[]` | +| `hostAliases` | ClickHouse pods host aliases | `[]` | +| `podLabels` | Extra labels for ClickHouse pods | `{}` | +| `podAnnotations` | Annotations for ClickHouse pods | `{}` | +| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `nodeAffinityPreset.key` | Node label key to match. Ignored if `affinity` is set | `""` | +| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set | `[]` | +| `affinity` | Affinity for ClickHouse pods assignment | `{}` | +| `nodeSelector` | Node labels for ClickHouse pods assignment | `{}` | +| `tolerations` | Tolerations for ClickHouse pods assignment | `[]` | +| `updateStrategy.type` | ClickHouse statefulset strategy type | `RollingUpdate` | +| `podManagementPolicy` | Statefulset Pod management policy, it needs to be Parallel to be able to complete the cluster join | `Parallel` | +| `priorityClassName` | ClickHouse pods' priorityClassName | `""` | +| `topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | +| `schedulerName` | Name of the k8s scheduler (other than default) for ClickHouse pods | `""` | +| `terminationGracePeriodSeconds` | Seconds Redmine pod needs to terminate gracefully | `""` | +| `lifecycleHooks` | for the ClickHouse container(s) to automate configuration before or after startup | `{}` | +| `extraEnvVars` | Array with extra environment variables to add to ClickHouse nodes | `[]` | +| `extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for ClickHouse nodes | `""` | +| `extraEnvVarsSecret` | Name of existing Secret containing extra env vars for ClickHouse nodes | `""` | +| `extraVolumes` | Optionally specify extra list of additional volumes for the ClickHouse pod(s) | `[]` | +| `extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the ClickHouse container(s) | `[]` | +| `sidecars` | Add additional sidecar containers to the ClickHouse pod(s) | `[]` | +| `initContainers` | Add additional init containers to the ClickHouse pod(s) | `[]` | +| `tls.enabled` | Enable TLS traffic support | `false` | +| `tls.autoGenerated` | Generate automatically self-signed TLS certificates | `false` | +| `tls.certificatesSecret` | Name of an existing secret that contains the certificates | `""` | +| `tls.certFilename` | Certificate filename | `""` | +| `tls.certKeyFilename` | Certificate key filename | `""` | +| `tls.certCAFilename` | CA Certificate filename | `""` | + +### Traffic Exposure Parameters + +| Name | Description | Value | +| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `service.type` | ClickHouse service type | `ClusterIP` | +| `service.ports.http` | ClickHouse service HTTP port | `8123` | +| `service.ports.https` | ClickHouse service HTTPS port | `443` | +| `service.ports.tcp` | ClickHouse service TCP port | `9000` | +| `service.ports.tcpSecure` | ClickHouse service TCP (secure) port | `9440` | +| `service.ports.keeper` | ClickHouse keeper TCP container port | `2181` | +| `service.ports.keeperSecure` | ClickHouse keeper TCP (secure) container port | `3181` | +| `service.ports.keeperInter` | ClickHouse keeper interserver TCP container port | `9444` | +| `service.ports.mysql` | ClickHouse service MySQL port | `9004` | +| `service.ports.postgresql` | ClickHouse service PostgreSQL port | `9005` | +| `service.ports.interserver` | ClickHouse service Interserver port | `9009` | +| `service.ports.metrics` | ClickHouse service metrics port | `8001` | +| `service.nodePorts.http` | Node port for HTTP | `""` | +| `service.nodePorts.https` | Node port for HTTPS | `""` | +| `service.nodePorts.tcp` | Node port for TCP | `""` | +| `service.nodePorts.tcpSecure` | Node port for TCP (with TLS) | `""` | +| `service.nodePorts.keeper` | ClickHouse keeper TCP container port | `""` | +| `service.nodePorts.keeperSecure` | ClickHouse keeper TCP (secure) container port | `""` | +| `service.nodePorts.keeperInter` | ClickHouse keeper interserver TCP container port | `""` | +| `service.nodePorts.mysql` | Node port for MySQL | `""` | +| `service.nodePorts.postgresql` | Node port for PostgreSQL | `""` | +| `service.nodePorts.interserver` | Node port for Interserver | `""` | +| `service.nodePorts.metrics` | Node port for metrics | `""` | +| `service.clusterIP` | ClickHouse service Cluster IP | `""` | +| `service.loadBalancerIP` | ClickHouse service Load Balancer IP | `""` | +| `service.loadBalancerSourceRanges` | ClickHouse service Load Balancer sources | `[]` | +| `service.externalTrafficPolicy` | ClickHouse service external traffic policy | `Cluster` | +| `service.annotations` | Additional custom annotations for ClickHouse service | `{}` | +| `service.extraPorts` | Extra ports to expose in ClickHouse service (normally used with the `sidecars` value) | `[]` | +| `service.sessionAffinity` | Control where client requests go, to the same pod or round-robin | `None` | +| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `service.headless.annotations` | Annotations for the headless service. | `{}` | +| `externalAccess.enabled` | Enable Kubernetes external cluster access to ClickHouse | `false` | +| `externalAccess.service.type` | Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP | `LoadBalancer` | +| `externalAccess.service.ports.http` | ClickHouse service HTTP port | `80` | +| `externalAccess.service.ports.https` | ClickHouse service HTTPS port | `443` | +| `externalAccess.service.ports.tcp` | ClickHouse service TCP port | `9000` | +| `externalAccess.service.ports.tcpSecure` | ClickHouse service TCP (secure) port | `9440` | +| `externalAccess.service.ports.keeper` | ClickHouse keeper TCP container port | `2181` | +| `externalAccess.service.ports.keeperSecure` | ClickHouse keeper TCP (secure) container port | `3181` | +| `externalAccess.service.ports.keeperInter` | ClickHouse keeper interserver TCP container port | `9444` | +| `externalAccess.service.ports.mysql` | ClickHouse service MySQL port | `9004` | +| `externalAccess.service.ports.postgresql` | ClickHouse service PostgreSQL port | `9005` | +| `externalAccess.service.ports.interserver` | ClickHouse service Interserver port | `9009` | +| `externalAccess.service.ports.metrics` | ClickHouse service metrics port | `8001` | +| `externalAccess.service.loadBalancerIPs` | Array of load balancer IPs for each ClickHouse . Length must be the same as replicaCount | `[]` | +| `externalAccess.service.loadBalancerAnnotations` | Array of load balancer annotations for each ClickHouse . Length must be the same as shards multiplied by replicaCount | `[]` | +| `externalAccess.service.loadBalancerSourceRanges` | Address(es) that are allowed when service is LoadBalancer | `[]` | +| `externalAccess.service.nodePorts.http` | Node port for HTTP | `[]` | +| `externalAccess.service.nodePorts.https` | Node port for HTTPS | `[]` | +| `externalAccess.service.nodePorts.tcp` | Node port for TCP | `[]` | +| `externalAccess.service.nodePorts.tcpSecure` | Node port for TCP (with TLS) | `[]` | +| `externalAccess.service.nodePorts.keeper` | ClickHouse keeper TCP container port | `[]` | +| `externalAccess.service.nodePorts.keeperSecure` | ClickHouse keeper TCP container port (with TLS) | `[]` | +| `externalAccess.service.nodePorts.keeperInter` | ClickHouse keeper interserver TCP container port | `[]` | +| `externalAccess.service.nodePorts.mysql` | Node port for MySQL | `[]` | +| `externalAccess.service.nodePorts.postgresql` | Node port for PostgreSQL | `[]` | +| `externalAccess.service.nodePorts.interserver` | Node port for Interserver | `[]` | +| `externalAccess.service.nodePorts.metrics` | Node port for metrics | `[]` | +| `externalAccess.service.labels` | Service labels for external access | `{}` | +| `externalAccess.service.annotations` | Service annotations for external access | `{}` | +| `externalAccess.service.extraPorts` | Extra ports to expose in the ClickHouse external service | `[]` | +| `ingress.enabled` | Enable ingress record generation for ClickHouse | `false` | +| `ingress.pathType` | Ingress path type | `ImplementationSpecific` | +| `ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `""` | +| `ingress.hostname` | Default host for the ingress record | `clickhouse.local` | +| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | +| `ingress.path` | Default path for the ingress record | `/` | +| `ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | +| `ingress.tls` | Enable TLS configuration for the host defined at `ingress.hostname` parameter | `false` | +| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | +| `ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | +| `ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | +| `ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | +| `ingress.secrets` | Custom TLS certificates as secrets | `[]` | +| `ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` | + +### Persistence Parameters + +| Name | Description | Value | +| --------------------------- | ----------------------------------------------------------------------- | ------------------- | +| `persistence.enabled` | Enable persistence using Persistent Volume Claims | `true` | +| `persistence.existingClaim` | Name of an existing PVC to use | `""` | +| `persistence.storageClass` | Storage class of backing PVC | `""` | +| `persistence.labels` | Persistent Volume Claim labels | `{}` | +| `persistence.annotations` | Persistent Volume Claim annotations | `{}` | +| `persistence.accessModes` | Persistent Volume Access Modes | `["ReadWriteOnce"]` | +| `persistence.size` | Size of data volume | `8Gi` | +| `persistence.selector` | Selector to match an existing Persistent Volume for ClickHouse data PVC | `{}` | +| `persistence.dataSource` | Custom PVC data source | `{}` | + +### Init Container Parameters + +| Name | Description | Value | +| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | -------------------------- | +| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` | +| `volumePermissions.image.registry` | OS Shell + Utility image registry | `REGISTRY_NAME` | +| `volumePermissions.image.repository` | OS Shell + Utility image repository | `REPOSITORY_NAME/os-shell` | +| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` | +| `volumePermissions.resources.limits` | The resources limits for the init container | `{}` | +| `volumePermissions.resources.requests` | The requested resources for the init container | `{}` | +| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` | + +### Other Parameters + +| Name | Description | Value | +| --------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------- | +| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | +| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | +| `serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | +| `metrics.enabled` | Enable the export of Prometheus metrics | `false` | +| `metrics.podAnnotations` | Annotations for metrics scraping | `{}` | +| `metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) | `false` | +| `metrics.serviceMonitor.namespace` | Namespace in which Prometheus is running | `""` | +| `metrics.serviceMonitor.annotations` | Additional custom annotations for the ServiceMonitor | `{}` | +| `metrics.serviceMonitor.labels` | Extra labels for the ServiceMonitor | `{}` | +| `metrics.serviceMonitor.jobLabel` | The name of the label on the target service to use as the job name in Prometheus | `""` | +| `metrics.serviceMonitor.honorLabels` | honorLabels chooses the metric's labels on collisions with target labels | `false` | +| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped. | `""` | +| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `""` | +| `metrics.serviceMonitor.metricRelabelings` | Specify additional relabeling of metrics | `[]` | +| `metrics.serviceMonitor.relabelings` | Specify general relabeling | `[]` | +| `metrics.serviceMonitor.selector` | Prometheus instance selector labels | `{}` | +| `metrics.prometheusRule.enabled` | Create a PrometheusRule for Prometheus Operator | `false` | +| `metrics.prometheusRule.namespace` | Namespace for the PrometheusRule Resource (defaults to the Release Namespace) | `""` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so PrometheusRule will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.rules` | PrometheusRule definitions | `[]` | + +### External Zookeeper paramaters + +| Name | Description | Value | +| --------------------------- | ----------------------------------------- | ------ | +| `externalZookeeper.servers` | List of external zookeeper servers to use | `[]` | +| `externalZookeeper.port` | Port of the Zookeeper servers | `2888` | + +### Zookeeper subchart parameters + +| Name | Description | Value | +| -------------------------------- | ----------------------------- | --------------------------- | +| `zookeeper.enabled` | Deploy Zookeeper subchart | `true` | +| `zookeeper.replicaCount` | Number of Zookeeper instances | `3` | +| `zookeeper.service.ports.client` | Zookeeper client port | `2181` | +| `zookeeper.image.registry` | Zookeeper image registry | `REGISTRY_NAME` | +| `zookeeper.image.repository` | Zookeeper image repository | `REPOSITORY_NAME/zookeeper` | +| `zookeeper.image.pullPolicy` | Zookeeper image pull policy | `IfNotPresent` | + +See to create the table. + +The above parameters map to the env variables defined in [bitnami/clickhouse](https://github.com/bitnami/containers/tree/main/bitnami/clickhouse). For more information please refer to the [bitnami/clickhouse](https://github.com/bitnami/containers/tree/main/bitnami/clickhouse) image documentation. + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +helm install my-release \ + --set auth.username=admin \ + --set auth.password=password \ + oci://REGISTRY_NAME/REPOSITORY_NAME/clickhouse +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +The above command sets the ClickHouse administrator account username and password to `admin` and `password` respectively. + +> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. + +Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, + +```console +helm install my-release -f values.yaml oci://REGISTRY_NAME/REPOSITORY_NAME/clickhouse +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. +> **Tip**: You can use the default [values.yaml](https://github.com/bitnami/charts/tree/main/bitnami/clickhouse/values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### ClickHouse keeper support + +You can set `keeper.enabled` to use ClickHouse keeper. If `keeper.enabled=true`, Zookeeper settings will not be ignore. + +### External Zookeeper support + +You may want to have ClickHouse connect to an external zookeeper rather than installing one inside your cluster. Typical reasons for this are to use a managed database service, or to share a common database server for all your applications. To achieve this, the chart allows you to specify credentials for an external database with the [`externalZookeeper` parameter](#parameters). You should also disable the Zookeeper installation with the `zookeeper.enabled` option. Here is an example: + +```console +zookeper.enabled=false +externalZookeeper.host=myexternalhost +externalZookeeper.user=myuser +externalZookeeper.password=mypassword +externalZookeeper.database=mydatabase +externalZookeeper.port=3306 +``` + +### TLS secrets + +The chart also facilitates the creation of TLS secrets for use with the Ingress controller, with different options for certificate management. [Learn more about TLS secrets](https://docs.bitnami.com/kubernetes/infrastructure/clickhouse/administration/enable-tls-ingress/)). + +## Persistence + +The [Bitnami ClickHouse](https://github.com/bitnami/containers/tree/main/bitnami/clickhouse) image stores the ClickHouse data and configurations at the `/bitnami` path of the container. Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. + +### Additional environment variables + +In case you want to add extra environment variables (useful for advanced operations like custom init scripts), you can use the `extraEnvVars` property. + +```yaml +clickhouse: + extraEnvVars: + - name: LOG_LEVEL + value: error +``` + +Alternatively, you can use a ConfigMap or a Secret with the environment variables. To do so, use the `extraEnvVarsCM` or the `extraEnvVarsSecret` values. + +### Sidecars + +If additional containers are needed in the same pod as ClickHouse (such as additional metrics or logging exporters), they can be defined using the `sidecars` parameter. If these sidecars export extra ports, extra port definitions can be added using the `service.extraPorts` parameter. [Learn more about configuring and using sidecar containers](https://docs.bitnami.com/kubernetes/infrastructure/clickhouse/configuration/configure-sidecar-init-containers/). + +### Ingress without TLS + +For using ingress (example without TLS): + +```yaml +ingress: + ## If true, ClickHouse server Ingress will be created + ## + enabled: true + + ## ClickHouse server Ingress annotations + ## + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## ClickHouse server Ingress hostnames + ## Must be provided if Ingress is enabled + ## + hosts: + - clickhouse.domain.com +``` + +### Ingress TLS + +If your cluster allows automatic creation/retrieval of TLS certificates (e.g. [kube-lego](https://github.com/jetstack/kube-lego)), please refer to the documentation for that mechanism. + +To manually configure TLS, first create/retrieve a key & certificate pair for the address(es) you wish to protect. Then create a TLS secret (named `clickhouse-server-tls` in this example) in the namespace. Include the secret's name, along with the desired hostnames, in the Ingress TLS section of your custom `values.yaml` file: + +```yaml +ingress: + ## If true, ClickHouse server Ingress will be created + ## + enabled: true + + ## ClickHouse server Ingress annotations + ## + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + + ## ClickHouse server Ingress hostnames + ## Must be provided if Ingress is enabled + ## + hosts: + - clickhouse.domain.com + + ## ClickHouse server Ingress TLS configuration + ## Secrets must be manually created in the namespace + ## + tls: + - secretName: clickhouse-server-tls + hosts: + - clickhouse.domain.com +``` + +### Using custom scripts + +For advanced operations, the Bitnami ClickHouse chart allows using custom init and start scripts that will be mounted in `/docker-entrypoint.initdb.d` and `/docker-entrypoint.startdb.d` . The `init` scripts will be run on the first boot whereas the `start` scripts will be run on every container start. For adding the scripts directly as values use the `initdbScripts` and `startdbScripts` values. For using Secrets use the `initdbScriptsSecret` and `startdbScriptsSecret`. + +```yaml +initdbScriptsSecret: init-scripts-secret +startdbScriptsSecret: start-scripts-secret +``` + +### Pod affinity + +This chart allows you to set your custom affinity using the `affinity` parameter. Find more information about Pod affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, use one of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +### To 2.0.0 + +This major updates the Zookeeper subchart to it newest major, 11.0.0. For more information on this subchart's major, please refer to [zookeeper upgrade notes](https://github.com/bitnami/charts/tree/main/bitnami/zookeeper#to-1100). + +## License + +Copyright © 2023 VMware, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/charts/clickhouse/templates/NOTES.txt b/charts/clickhouse/templates/NOTES.txt old mode 100755 new mode 100644 index f8a6dd14..4bb61dab --- a/charts/clickhouse/templates/NOTES.txt +++ b/charts/clickhouse/templates/NOTES.txt @@ -1,31 +1,58 @@ -** Please be patient while the chart is being deployed ** - -1. Get the Clickhouse URL by running: - -{{- if .Values.clickhouse.ingress.enabled }} - - export HOSTNAME=$(kubectl get ingress --namespace {{ .Release.Namespace }} {{ template "clickhouse.fullname" . }} -o jsonpath='{.spec.rules[0].host}') - echo "Clickhouse URL: http://$HOSTNAME/" - -{{- else }} - - echo URL : http://127.0.0.1:8080/ - echo Management URL : http://127.0.0.1:8080/manager - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "clickhouse.fullname" . }} 8123:{{ .Values.clickhouse.http_port }} - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "clickhouse.fullname" . }} 9000:{{ .Values.clickhouse.tcp_port }} - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "clickhouse.fullname" . }} 9009:{{ .Values.clickhouse.interserver_http_port }} - -{{- end }} - -2. Get the Tabix URL by running: - -{{- if .Values.tabix.ingress.enabled }} - - export HOSTNAME=$(kubectl get ingress --namespace {{ .Release.Namespace }} {{ template "clickhouse.fullname" . }}-tabix -o jsonpath='{.spec.rules[0].host}') - echo "Tabix URL: http://$HOSTNAME/" - -{{- else }} - - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "clickhouse.fullname" . }}-tabix 80 - -{{- end }} +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +** Please be patient while the chart is being deployed ** + +{{- if .Values.diagnosticMode.enabled }} +The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: + + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} + +Get the list of pods by executing: + + kubectl get pods --namespace {{ include "common.names.namespace" . | quote }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Access the pod you want to debug by executing + + kubectl exec --namespace {{ include "common.names.namespace" . | quote }} -ti -- bash + +In order to replicate the container startup scripts execute this command: + + /opt/bitnami/scripts/clickhouse/entrypoint.sh /opt/bitnami/scripts/clickhouse/run.sh + +{{- else }} + +ClickHouse is available in the following address: + +{{- if .Values.externalAccess.enabled }} + +NOTE: It may take a few minutes for the LoadBalancer IP to be available. + + kubectl get svc --namespace {{ template "common.names.namespace" . }} -l "app.kubernetes.io/name={{ template "common.names.fullname" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=clickhouse" -w + +{{- else if (eq "LoadBalancer" .Values.service.type) }} + + export SERVICE_IP=$(kubectl get svc --namespace {{ template "common.names.namespace" . }} {{ template "common.names.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + +{{- else if (eq "NodePort" .Values.service.type)}} + + export NODE_IP=$(kubectl get nodes --namespace {{ template "common.names.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ template "common.names.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "common.names.fullname" . }}) + +{{- else if (eq "ClusterIP" .Values.service.type)}} + + kubectl port-forward --namespace {{ template "common.names.namespace" . }} svc/{{ template "common.names.fullname" . }} {{ .Values.service.ports.tcp }}:9000 & + +{{- end }} + +Credentials: + + echo "Username : {{ .Values.auth.username }}" + echo "Password : $(kubectl get secret --namespace {{ .Release.Namespace }} {{ include "clickhouse.secretName" . }} -o jsonpath="{.data.{{ include "clickhouse.secretKey" .}}}" | base64 -d)" + +{{- end }} + +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "clickhouse.validateValues" . }} diff --git a/charts/clickhouse/templates/_helpers.tpl b/charts/clickhouse/templates/_helpers.tpl old mode 100755 new mode 100644 index e6690cdf..b5243526 --- a/charts/clickhouse/templates/_helpers.tpl +++ b/charts/clickhouse/templates/_helpers.tpl @@ -1,56 +1,219 @@ -{{/* vim: set filetype=mustache: */}} {{/* -Expand the name of the chart. +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 */}} -{{- define "clickhouse.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} + +{{/* +Return the proper ClickHouse image name +*/}} +{{- define "clickhouse.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} {{- end -}} {{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "clickhouse.volumePermissions.image" -}} +{{- include "common.images.image" ( dict "imageRoot" .Values.volumePermissions.image "global" .Values.global ) -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "clickhouse.imagePullSecrets" -}} +{{- include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.volumePermissions.image) "global" .Values.global) -}} +{{- end -}} + +{{/* +Return true if a TLS credentials secret object should be created */}} -{{- define "clickhouse.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- define "clickhouse.createTlsSecret" -}} +{{- if and .Values.tls.autoGenerated (not .Values.tls.certificatesSecret) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "clickhouse.tlsSecretName" -}} +{{- if .Values.tls.autoGenerated }} + {{- printf "%s-crt" (include "common.names.fullname" .) -}} {{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} + {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert file. +*/}} +{{- define "clickhouse.tlsCert" -}} +{{- if .Values.tls.autoGenerated }} + {{- printf "/opt/bitnami/clickhouse/certs/tls.crt" -}} {{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} + {{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/clickhouse/certs/%s" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert key file. +*/}} +{{- define "clickhouse.tlsCertKey" -}} +{{- if .Values.tls.autoGenerated }} + {{- printf "/opt/bitnami/clickhouse/certs/tls.key" -}} +{{- else -}} +{{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/clickhouse/certs/%s" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "clickhouse.tlsCACert" -}} +{{- if .Values.tls.autoGenerated }} + {{- printf "/opt/bitnami/clickhouse/certs/ca.crt" -}} +{{- else -}} + {{- printf "/opt/bitnami/clickhouse/certs/%s" .Values.tls.certCAFilename -}} +{{- end -}} +{{- end -}} + +{{/* +Get the ClickHouse configuration configmap. +*/}} +{{- define "clickhouse.configmapName" -}} +{{- if .Values.existingOverridesConfigmap -}} + {{- .Values.existingOverridesConfigmap -}} +{{- else }} + {{- printf "%s" (include "common.names.fullname" . ) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the ClickHouse configuration configmap. +*/}} +{{- define "clickhouse.extraConfigmapName" -}} +{{- if .Values.extraOverridesConfigmap -}} + {{- .Values.extraOverridesConfigmap -}} +{{- else }} + {{- printf "%s-extra" (include "common.names.fullname" . ) -}} +{{- end -}} {{- end -}} + + +{{/* +Get the ClickHouse configuration users configmap. +*/}} +{{- define "clickhouse.usersExtraConfigmapName" -}} +{{- if .Values.usersExtraOverridesConfigmap -}} + {{- .Values.usersExtraOverridesConfigmap -}} +{{- else }} + {{- printf "%s-users-extra" (include "common.names.fullname" . ) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the Clickhouse password secret name +*/}} +{{- define "clickhouse.secretName" -}} +{{- if .Values.auth.existingSecret -}} + {{- .Values.auth.existingSecret -}} +{{- else }} + {{- printf "%s" (include "common.names.fullname" . ) -}} {{- end -}} {{- end -}} {{/* -Create chart name and version as used by the chart label. +Get the ClickHouse password key inside the secret */}} -{{- define "clickhouse.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- define "clickhouse.secretKey" -}} +{{- if .Values.auth.existingSecret -}} + {{- .Values.auth.existingSecretKey -}} +{{- else }} + {{- print "admin-password" -}} +{{- end -}} {{- end -}} {{/* -Create clickhouse path. -if .Values.clickhouse.path is empty, default value "/var/lib/clickhouse". +Get the startialization scripts Secret name. */}} -{{- define "clickhouse.fullpath" -}} -{{- if .Values.clickhouse.path -}} -{{- .Values.clickhouse.path | trunc 63 | trimSuffix "-" -}} +{{- define "clickhouse.startdbScriptsSecret" -}} +{{- if .Values.startdbScriptsSecret -}} + {{- printf "%s" (tpl .Values.startdbScriptsSecret $) -}} {{- else -}} -{{- printf "%s" "/var/lib/clickhouse" -}} + {{- printf "%s-start-scripts" (include "common.names.fullname" .) -}} {{- end -}} {{- end -}} {{/* -Create clickhouse log path. -if .Values.clickhouse.configmap.logger.path is empty, default value "/var/log/clickhouse-server". +Get the initialization scripts Secret name. */}} -{{- define "clickhouse.logpath" -}} -{{- if .Values.clickhouse.configmap.logger.path -}} -{{- .Values.clickhouse.configmap.logger.path | trunc 63 | trimSuffix "-" -}} +{{- define "clickhouse.initdbScriptsSecret" -}} +{{- if .Values.initdbScriptsSecret -}} + {{- printf "%s" (tpl .Values.initdbScriptsSecret $) -}} {{- else -}} -{{- printf "%s" "/var/log/clickhouse-server" -}} + {{- printf "%s-init-scripts" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "clickhouse.headlessServiceName" -}} +{{- printf "%s-headless" (include "common.names.fullname" .) -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "clickhouse.zookeeper.fullname" -}} +{{- include "common.names.dependency.fullname" (dict "chartName" "zookeeper" "chartValues" .Values.zookeeper "context" $) -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "clickhouse.zookeeper.headlessServiceName" -}} +{{- printf "%s-headless" (include "clickhouse.zookeeper.fullname" .) -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "clickhouse.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message. +*/}} +{{- define "clickhouse.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "clickhouse.validateValues.zookeeper" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message -}} +{{- end -}} +{{- end -}} + +{{/* Validate values of ClickHouse - [Zoo]keeper */}} +{{- define "clickhouse.validateValues.zookeeper" -}} +{{- if or (and .Values.keeper.enabled .Values.zookeeper.enabled) (and .Values.keeper.enabled .Values.externalZookeeper.servers) (and .Values.zookeeper.enabled .Values.externalZookeeper.servers) -}} +clickhouse: Multiple [Zoo]keeper + You can only use one [zoo]keeper + Please choose use ClickHouse keeper or + installing a Zookeeper chart (--set zookeeper.enabled=true) or + using an external instance (--set zookeeper.servers ) +{{- end -}} +{{- if and (not .Values.keeper.enabled) (not .Values.zookeeper.enabled) (not .Values.externalZookeeper.servers) (ne (int .Values.shards) 1) (ne (int .Values.replicaCount) 1) -}} +clickhouse: No [Zoo]keeper + If you are deploying more than one ClickHouse instance, you need to enable [Zoo]keeper. Please choose installing a [Zoo]keeper (--set keeper.enabled=true) or (--set zookeeper.enabled=true) or + using an external instance (--set zookeeper.servers ) {{- end -}} {{- end -}} diff --git a/charts/clickhouse/templates/configmap-config.yaml b/charts/clickhouse/templates/configmap-config.yaml deleted file mode 100755 index 3bfae41e..00000000 --- a/charts/clickhouse/templates/configmap-config.yaml +++ /dev/null @@ -1,112 +0,0 @@ -{{- if .Values.clickhouse.configmap.enabled }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "clickhouse.fullname" . }}-config - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-config - app.kubernetes.io/instance: {{ .Release.Name }}-config - app.kubernetes.io/managed-by: {{ .Release.Service }} -data: - config.xml: |- - - - {{ include "clickhouse.fullpath" . }}/ - {{ printf "%s/tmp/" (include "clickhouse.fullpath" .) }} - {{ printf "%s/user_files/" (include "clickhouse.fullpath" .) }} - {{ printf "%s/format_schemas/" (include "clickhouse.fullpath" .) }} - - /etc/clickhouse-server/metrica.d/metrica.xml - - users.xml - - {{ template "clickhouse.fullname" . }} - 0.0.0.0 - {{ .Values.clickhouse.http_port | default "8123" }} - {{ .Values.clickhouse.tcp_port | default "9000" }} - {{ .Values.clickhouse.interserver_http_port | default "9009" }} - {{ .Values.clickhouse.configmap.max_connections | default "4096" }} - {{ .Values.clickhouse.configmap.keep_alive_timeout | default "3" }} - {{ .Values.clickhouse.configmap.max_concurrent_queries | default "100" }} - {{ .Values.clickhouse.configmap.uncompressed_cache_size | default "8589934592" }} - {{ .Values.clickhouse.configmap.mark_cache_size | default "5368709120" }} - {{ .Values.timezone | default "Asia/Shanghai" }} - {{ .Values.clickhouse.configmap.umask | default "027" }} - {{ .Values.clickhouse.configmap.mlock_executable | default "false" }} - - - - {{ .Values.clickhouse.configmap.builtin_dictionaries_reload_interval | default "3600" }} - {{ .Values.clickhouse.configmap.max_session_timeout | default "3600" }} - {{ .Values.clickhouse.configmap.default_session_timeout | default "60" }} - {{ .Values.clickhouse.configmap.disable_internal_dns_cache | default "1" }} - - - system - query_log
- toYYYYMM(event_date) - 7500 -
- - - system - query_thread_log
- toYYYYMM(event_date) - 7500 -
- - - /clickhouse/task_queue/ddl - - - {{- if .Values.clickhouse.configmap.logger }} - - {{ .Values.clickhouse.configmap.logger.level | default "trace" }} - {{ printf "%s/%s" (include "clickhouse.logpath" .) "clickhouse-server.log" }} - {{ printf "%s/%s" (include "clickhouse.logpath" .) "clickhouse-server.err.log" }} - {{ .Values.clickhouse.configmap.logger.size | default "1000M" }} - {{ .Values.clickhouse.configmap.logger.count | default "10" }} - - {{- end }} - - {{- if .Values.clickhouse.configmap.compression.enabled }} - - {{- range .Values.clickhouse.configmap.compression.cases }} - {{- with . }} - - {{ .min_part_size }} - {{ .min_part_size_ratio }} - {{ .method }} - - {{- end }} - {{- end }} - - {{- end }} - - {{- if .Values.clickhouse.configmap.graphite.enabled }} - {{- range .Values.clickhouse.configmap.graphite.config }} - {{- with . }} - - {{ template "clickhouse.fullname" $ }}-graphite.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }} - {{ $.Values.clickhouse.graphite.service.port }} - {{ .timeout | default "0.1" }} - {{ .interval | default "60" }} - {{ .root_path | default "one_min" }} - {{ .metrics | default "true" }} - {{ .events | default "true" }} - {{ .events_cumulative | default "true" }} - {{ .asynchronous_metrics | default "true" }} - - {{- end }} - {{- end }} - {{- end }} - - {{- if .Values.clickhouse.configmap.max_open_files }} - {{ .Values.clickhouse.configmap.max_open_files }} - {{- end }} - - {{- if .Values.clickhouse.configmap.interserver_http_host }} - {{ .Values.clickhouse.configmap.interserver_http_host }} - {{- end }} -
-{{- end }} diff --git a/charts/clickhouse/templates/configmap-extra.yaml b/charts/clickhouse/templates/configmap-extra.yaml new file mode 100644 index 00000000..153cf4d5 --- /dev/null +++ b/charts/clickhouse/templates/configmap-extra.yaml @@ -0,0 +1,20 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.extraOverrides (not .Values.extraOverridesConfigmap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-extra" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + 01_extra_overrides.xml: | + {{- include "common.tplvalues.render" (dict "value" .Values.extraOverrides "context" $) | nindent 4 }} +{{- end }} diff --git a/charts/clickhouse/templates/configmap-metrika.yaml b/charts/clickhouse/templates/configmap-metrika.yaml deleted file mode 100755 index 2d14bc99..00000000 --- a/charts/clickhouse/templates/configmap-metrika.yaml +++ /dev/null @@ -1,77 +0,0 @@ -{{- if .Values.clickhouse.configmap.enabled }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "clickhouse.fullname" . }}-metrica - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-metrica - app.kubernetes.io/instance: {{ .Release.Name }}-metrica - app.kubernetes.io/managed-by: {{ .Release.Service }} -data: - metrica.xml: |- - - - {{- if .Values.clickhouse.configmap.zookeeper_servers.enabled }} - - {{- range .Values.clickhouse.configmap.zookeeper_servers.config }} - {{- with . }} - - {{ .host }} - {{ .port }} - - {{- end }} - {{- end }} - {{ .Values.clickhouse.configmap.zookeeper_servers.session_timeout_ms }} - {{ .Values.clickhouse.configmap.zookeeper_servers.operation_timeout_ms }} - {{ .Values.clickhouse.configmap.zookeeper_servers.root }} - {{ .Values.clickhouse.configmap.zookeeper_servers.identity }} - - {{- end }} - - {{- if .Values.clickhouse.configmap.remote_servers.enabled }} - - <{{ include "clickhouse.fullname" . }}> - {{- range untilStep 0 (int .Values.clickhouse.replicas) 1 }} - - - {{ $.Values.clickhouse.configmap.remote_servers.internal_replication | default "false" }} - {{ include "clickhouse.fullname" $ }}-{{ . }}.{{ include "clickhouse.fullname" $ }}-headless.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }} - {{ $.Values.clickhouse.tcp_port}} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.user }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.user }} - {{- end }} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.password }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.password }} - {{- end }} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.secure }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.secure }} - {{- end }} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.compression }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.compression }} - {{- end }} - - {{- if $.Values.clickhouse.configmap.remote_servers.replica.backup.enabled }} - - {{ include "clickhouse.fullname" $ }}-replica-{{ . }}.{{ include "clickhouse.fullname" $ }}-replica-headless.{{ $.Release.Namespace }}.svc.{{ $.Values.clusterDomain }} - {{ $.Values.clickhouse.tcp_port}} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.user }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.user }} - {{- end }} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.password }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.password }} - {{- end }} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.secure }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.secure }} - {{- end }} - {{- if $.Values.clickhouse.configmap.remote_servers.replica.compression }} - {{ $.Values.clickhouse.configmap.remote_servers.replica.compression }} - {{- end }} - - {{- end }} - - {{- end }} - - - {{- end }} - -{{- end }} diff --git a/charts/clickhouse/templates/configmap-users-extra.yaml b/charts/clickhouse/templates/configmap-users-extra.yaml new file mode 100644 index 00000000..056d2d02 --- /dev/null +++ b/charts/clickhouse/templates/configmap-users-extra.yaml @@ -0,0 +1,20 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.usersExtraOverrides (not .Values.usersExtraOverridesConfigmap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-users-extra" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + 01_users_extra_overrides.xml: | + {{- include "common.tplvalues.render" (dict "value" .Values.usersExtraOverrides "context" $) | nindent 4 }} +{{- end }} diff --git a/charts/clickhouse/templates/configmap-users.yaml b/charts/clickhouse/templates/configmap-users.yaml deleted file mode 100755 index 99dbdc3c..00000000 --- a/charts/clickhouse/templates/configmap-users.yaml +++ /dev/null @@ -1,68 +0,0 @@ -{{- if .Values.clickhouse.configmap.enabled }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "clickhouse.fullname" . }}-users - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-users - app.kubernetes.io/instance: {{ .Release.Name }}-users - app.kubernetes.io/managed-by: {{ .Release.Service }} -data: - users.xml: |- - - - {{- if .Values.clickhouse.configmap.profiles.enabled }} - - {{- range .Values.clickhouse.configmap.profiles.profile }} - {{- with . }} - <{{ .name }}> - {{- range $k_1, $v_1 := .config }} - <{{ $k_1 }}>{{ $v_1 }} - {{- end }} - - {{- end }} - {{- end }} - - {{- end }} - - {{- if .Values.clickhouse.configmap.users.enabled }} - - {{- range $key, $value := .Values.clickhouse.configmap.users.user }} - {{- with . }} - <{{ .name }}> - {{- range $k_1, $v_1 := .config }} - {{- if (eq "networks" $k_1) }} - - {{- range $v_1 }} - {{- with .}} - {{ . }} - {{- end }} - {{- end }} - - {{- else }} - <{{ $k_1 }}>{{ $v_1 }} - {{- end }} - {{- end }} - - {{- end }} - {{- end }} - - {{- end }} - - {{- if .Values.clickhouse.configmap.quotas.enabled }} - - {{- range $key, $value := .Values.clickhouse.configmap.quotas.quota }} - {{- with . }} - <{{ .name }}> - {{- range $val := .config }} - {{- range $k_1, $v_1 := $val }} - <{{ $k_1 }}>{{ $v_1 }} - {{- end }} - {{- end }} - - {{- end }} - {{- end }} - - {{- end }} - -{{- end }} diff --git a/charts/clickhouse/templates/configmap.yaml b/charts/clickhouse/templates/configmap.yaml new file mode 100644 index 00000000..2462712b --- /dev/null +++ b/charts/clickhouse/templates/configmap.yaml @@ -0,0 +1,20 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if not .Values.existingOverridesConfigmap }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + 00_default_overrides.xml: | + {{- include "common.tplvalues.render" (dict "value" .Values.defaultConfigurationOverrides "context" $) | nindent 4 }} +{{- end }} diff --git a/charts/clickhouse/templates/deployment-tabix.yaml b/charts/clickhouse/templates/deployment-tabix.yaml deleted file mode 100755 index e3c9e453..00000000 --- a/charts/clickhouse/templates/deployment-tabix.yaml +++ /dev/null @@ -1,85 +0,0 @@ -{{- if .Values.tabix.enabled }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "clickhouse.fullname" . }}-tabix - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-tabix - app.kubernetes.io/instance: {{ .Release.Name }}-tabix - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.tabix.replicas }} - selector: - matchLabels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-tabix - app.kubernetes.io/instance: {{ .Release.Name }}-tabix -{{- if .Values.tabix.updateStrategy }} - strategy: - type: {{ .Values.tabix.updateStrategy.type }} - rollingUpdate: - maxSurge: {{ .Values.tabix.updateStrategy.maxSurge }} - maxUnavailable: {{ .Values.tabix.updateStrategy.maxUnavailable }} -{{- end }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-tabix - app.kubernetes.io/instance: {{ .Release.Name }}-tabix - spec: - {{- if .Values.affinity }} - affinity: -{{ toYaml .Values.affinity | indent 8 }} - {{- end }} - {{- if .Values.tabix.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.tabix.imagePullSecrets }} - - name: {{ . | quote }} - {{- end }} - {{- end }} - containers: - - name: {{ include "clickhouse.name" . }}-tabix - image: {{ .Values.tabix.image }}:{{ .Values.tabix.imageVersion }} - imagePullPolicy: {{ .Values.tabix.imagePullPolicy }} - ports: - - name: http - containerPort: 80 - env: - {{- if .Values.tabix.security }} - - name: USER - value: {{ .Values.tabix.security.user }} - - name: PASSWORD - value: {{ .Values.tabix.security.password }} - {{- end }} - {{- if .Values.tabix.automaticConnection }} - - name: CH_NAME - value: {{ .Values.tabix.automaticConnection.chName }} - - name: CH_HOST - value: {{ .Values.tabix.automaticConnection.chHost }} - - name: CH_LOGIN - value: {{ .Values.tabix.automaticConnection.chLogin }} - - name: CH_PASSWORD - value: {{ .Values.tabix.automaticConnection.chPassword }} - - name: CH_PARAMS - value: {{ .Values.tabix.automaticConnection.chParams }} - {{- end }} - {{- if .Values.tabix.livenessProbe.enabled }} - livenessProbe: - tcpSocket: - port: 80 - initialDelaySeconds: {{ .Values.tabix.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.tabix.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.tabix.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.tabix.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.tabix.livenessProbe.successThreshold }} - {{- end }} - {{- if .Values.tabix.readinessProbe.enabled }} - readinessProbe: - tcpSocket: - port: 80 - initialDelaySeconds: {{ .Values.tabix.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.tabix.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.tabix.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.tabix.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.tabix.readinessProbe.successThreshold }} - {{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/extra-list.yaml b/charts/clickhouse/templates/extra-list.yaml new file mode 100644 index 00000000..2d35a580 --- /dev/null +++ b/charts/clickhouse/templates/extra-list.yaml @@ -0,0 +1,9 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/charts/clickhouse/templates/ingress-clickhouse.yaml b/charts/clickhouse/templates/ingress-clickhouse.yaml deleted file mode 100755 index a4a672b7..00000000 --- a/charts/clickhouse/templates/ingress-clickhouse.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{- if .Values.clickhouse.ingress.enabled}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ include "clickhouse.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - rules: - host: {{ .Values.clickhouse.ingress.host }} - http: - paths: - - path: {{ .Values.clickhouse.ingress.path }} - backend: - serviceName: {{ include "clickhouse.fullname" . }} - servicePort: http -{{- if .Values.clickhouse.ingress.tls.enabled }} - tls: - hosts: - {{- range .Values.clickhouse.ingress.tls.hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .Values.clickhouse.ingress.tls.secretName }} -{{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/ingress-tabix.yaml b/charts/clickhouse/templates/ingress-tabix.yaml deleted file mode 100755 index 3b85c886..00000000 --- a/charts/clickhouse/templates/ingress-tabix.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if .Values.tabix.enabled }} -{{- if .Values.tabix.ingress.enabled}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ include "clickhouse.fullname" . }}-tabix - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-tabix - app.kubernetes.io/instance: {{ .Release.Name }}-tabix - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - rules: - host: {{ .Values.tabix.ingress.host }} - http: - paths: - - path: {{ .Values.tabix.ingress.path }} - backend: - serviceName: {{ include "clickhouse.fullname" . }}-tabix - servicePort: http -{{- if .Values.tabix.ingress.tls.enabled }} - tls: - hosts: - {{- range .Values.tabix.ingress.tls.hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .Values.tabix.ingress.tls.secretName }} -{{- end }} -{{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/ingress-tls-secrets.yaml b/charts/clickhouse/templates/ingress-tls-secrets.yaml new file mode 100644 index 00000000..6ef20e36 --- /dev/null +++ b/charts/clickhouse/templates/ingress-tls-secrets.yaml @@ -0,0 +1,44 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.ingress.enabled }} +{{- if .Values.ingress.secrets }} +{{- range .Values.ingress.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .name }} + namespace: {{ $.Release.Namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ .certificate | b64enc }} + tls.key: {{ .key | b64enc }} +--- +{{- end }} +{{- end }} +{{- if and .Values.ingress.tls .Values.ingress.selfSigned }} +{{- $secretName := printf "%s-tls" .Values.ingress.hostname }} +{{- $ca := genCA "clickhouse-ca" 365 }} +{{- $cert := genSignedCert .Values.ingress.hostname nil (list .Values.ingress.hostname) 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} +{{- end }} diff --git a/charts/clickhouse/templates/ingress.yaml b/charts/clickhouse/templates/ingress.yaml new file mode 100644 index 00000000..7000eceb --- /dev/null +++ b/charts/clickhouse/templates/ingress.yaml @@ -0,0 +1,59 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.ingress.enabled }} +apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.ingress.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.ingress.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} + ingressClassName: {{ .Values.ingress.ingressClassName | quote }} + {{- end }} + rules: + {{- if .Values.ingress.hostname }} + - host: {{ .Values.ingress.hostname | quote }} + http: + paths: + {{- if .Values.ingress.extraPaths }} + {{- toYaml .Values.ingress.extraPaths | nindent 10 }} + {{- end }} + - path: {{ .Values.ingress.path }} + {{- if eq "true" (include "common.ingress.supportsPathType" .) }} + pathType: {{ .Values.ingress.pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- range .Values.ingress.extraHosts }} + - host: {{ .name | quote }} + http: + paths: + - path: {{ default "/" .path }} + {{- if eq "true" (include "common.ingress.supportsPathType" $) }} + pathType: {{ default "ImplementationSpecific" .pathType }} + {{- end }} + backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" $) "servicePort" "http" "context" $) | nindent 14 }} + {{- end }} + {{- if .Values.ingress.extraRules }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraRules "context" $) | nindent 4 }} + {{- end }} + {{- if or (and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned)) .Values.ingress.extraTls }} + tls: + {{- if and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned) }} + - hosts: + - {{ .Values.ingress.hostname | quote }} + secretName: {{ printf "%s-tls" .Values.ingress.hostname }} + {{- end }} + {{- if .Values.ingress.extraTls }} + {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraTls "context" $) | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/clickhouse/templates/init-scripts-secret.yaml b/charts/clickhouse/templates/init-scripts-secret.yaml new file mode 100644 index 00000000..32367093 --- /dev/null +++ b/charts/clickhouse/templates/init-scripts-secret.yaml @@ -0,0 +1,19 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.initdbScripts (not .Values.initdbScriptsSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-init-scripts" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +stringData: +{{- include "common.tplvalues.render" (dict "value" .Values.initdbScripts "context" .) | nindent 2 }} +{{- end }} diff --git a/charts/clickhouse/templates/prometheusrule.yaml b/charts/clickhouse/templates/prometheusrule.yaml new file mode 100644 index 00000000..dc2d05d3 --- /dev/null +++ b/charts/clickhouse/templates/prometheusrule.yaml @@ -0,0 +1,24 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled .Values.metrics.prometheusRule.rules }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ default .Release.Namespace .Values.metrics.prometheusRule.namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.metrics.prometheusRule.additionalLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.additionalLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + groups: + - name: {{ include "common.names.fullname" . }} + rules: {{- toYaml .Values.metrics.prometheusRule.rules | nindent 8 }} +{{- end }} diff --git a/charts/clickhouse/templates/scripts-configmap.yaml b/charts/clickhouse/templates/scripts-configmap.yaml new file mode 100644 index 00000000..86aa34dc --- /dev/null +++ b/charts/clickhouse/templates/scripts-configmap.yaml @@ -0,0 +1,34 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + setup.sh: |- + #!/bin/bash + + # Execute entrypoint as usual after obtaining KEEPER_SERVER_ID + # check KEEPER_SERVER_ID in persistent volume via myid + # if not present, set based on POD hostname + if [[ -f "/bitnami/clickhouse/keeper/data/myid" ]]; then + export KEEPER_SERVER_ID="$(cat /bitnami/clickhouse/keeper/data/myid)" + else + HOSTNAME="$(hostname -s)" + if [[ $HOSTNAME =~ (.*)-([0-9]+)$ ]]; then + export KEEPER_SERVER_ID=${BASH_REMATCH[2]} + else + echo "Failed to get index from hostname $HOST" + exit 1 + fi + fi + exec /opt/bitnami/scripts/clickhouse/entrypoint.sh /opt/bitnami/scripts/clickhouse/run.sh -- --listen_host=0.0.0.0 diff --git a/charts/clickhouse/templates/service-account.yaml b/charts/clickhouse/templates/service-account.yaml new file mode 100644 index 00000000..649086da --- /dev/null +++ b/charts/clickhouse/templates/service-account.yaml @@ -0,0 +1,19 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "clickhouse.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if or .Values.serviceAccount.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.serviceAccount.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/charts/clickhouse/templates/service-external-access.yaml b/charts/clickhouse/templates/service-external-access.yaml new file mode 100644 index 00000000..f50baa21 --- /dev/null +++ b/charts/clickhouse/templates/service-external-access.yaml @@ -0,0 +1,155 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if $.Values.externalAccess.enabled }} +{{- $shards := $.Values.shards | int }} +{{- $replicas := $.Values.replicaCount | int }} +{{- $totalNodes := mul $shards $replicas }} +{{- range $shard, $e := until $shards }} +{{- range $i, $_e := until $replicas }} +{{- $loadBalancerAnnotationPosOffset := mul $shard $replicas }} +{{- $loadBalancerAnnotationPosition := add $loadBalancerAnnotationPosOffset $i }} +{{- $targetPod := printf "%s-shard%d-%d" (include "common.names.fullname" $) $shard $i }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-external" $targetPod | trunc 63 | trimSuffix "-" }} + namespace: {{ $.Release.Namespace | quote }} + {{- $labels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.externalAccess.service.labels $.Values.commonLabels ) "context" $ ) }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $labels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + pod: {{ $targetPod }} + {{- if or $.Values.externalAccess.service.annotations $.Values.commonAnnotations $.Values.externalAccess.service.loadBalancerAnnotations }} + annotations: + {{- if and (not (empty $.Values.externalAccess.service.loadBalancerAnnotations)) (eq (len $.Values.externalAccess.service.loadBalancerAnnotations) $totalNodes) }} + {{ include "common.tplvalues.render" ( dict "value" (index $.Values.externalAccess.service.loadBalancerAnnotations $loadBalancerAnnotationPosition) "context" $) | nindent 4 }} + {{- end }} + {{- if $.Values.externalAccess.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.externalAccess.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ $.Values.externalAccess.service.type }} + {{- if eq $.Values.externalAccess.service.type "LoadBalancer" }} + {{- if and (not (empty $.Values.externalAccess.service.loadBalancerIPs)) (eq (len $.Values.externalAccess.service.loadBalancerIPs) $totalNodes) }} + loadBalancerIP: {{ index $.Values.externalAccess.service.loadBalancerIPs $i }} + {{- end }} + {{- if $.Values.externalAccess.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml $.Values.externalAccess.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + ports: + - name: http + port: {{ $.Values.externalAccess.service.ports.http }} + targetPort: http + {{- if not (empty $.Values.externalAccess.service.nodePorts.http) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.http $i }} + {{- else }} + nodePort: null + {{- end }} + {{- if $.Values.tls.enabled }} + - name: https + port: {{ $.Values.externalAccess.service.ports.https }} + targetPort: https + {{- if not (empty $.Values.externalAccess.service.nodePorts.https) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.https $i }} + {{- else }} + nodePort: null + {{- end }} + {{- end }} + {{- if $.Values.metrics.enabled }} + - name: http-metrics + port: {{ $.Values.externalAccess.service.ports.metrics }} + targetPort: http-metrics + {{- if not (empty $.Values.externalAccess.service.nodePorts.metrics) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.metrics $i }} + {{- else }} + nodePort: null + {{- end }} + {{- end }} + - name: tcp + port: {{ $.Values.externalAccess.service.ports.tcp }} + targetPort: tcp + {{- if not (empty $.Values.externalAccess.service.nodePorts.tcp) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.tcp $i }} + {{- else }} + nodePort: null + {{- end }} + {{- if $.Values.tls.enabled }} + - name: tcp-secure + port: {{ $.Values.externalAccess.service.ports.tcpSecure }} + targetPort: tcp-secure + {{- if not (empty $.Values.externalAccess.service.nodePorts.tcpSecure) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.tcpSecure $i }} + {{- else }} + nodePort: null + {{- end }} + {{- end }} + {{- if $.Values.keeper.enabled }} + - name: tcp-keeper + port: {{ $.Values.externalAccess.service.ports.keeper }} + targetPort: tcp-keeper + {{- if not (empty $.Values.externalAccess.service.nodePorts.keeper) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.keeper $i }} + {{- else }} + nodePort: null + {{- end }} + - name: tcp-keeperinter + port: {{ $.Values.externalAccess.service.ports.keeperInter }} + targetPort: tcp-keeperinter + {{- if not (empty $.Values.externalAccess.service.nodePorts.keeperInter) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.keeperInter $i }} + {{- else }} + nodePort: null + {{- end }} + {{- if $.Values.tls.enabled }} + - name: tcp-keepertls + port: {{ $.Values.externalAccess.service.ports.keeperSecure }} + targetPort: tcp-keepertls + {{- if not (empty $.Values.externalAccess.service.nodePorts.keeperSecure) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.keeperSecure $i }} + {{- else }} + nodePort: null + {{- end }} + {{- end }} + {{- end }} + - name: tcp-mysql + port: {{ $.Values.externalAccess.service.ports.mysql }} + targetPort: tcp-mysql + {{- if not (empty $.Values.externalAccess.service.nodePorts.mysql) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.mysql $i }} + {{- else }} + nodePort: null + {{- end }} + - name: tcp-postgresql + port: {{ $.Values.externalAccess.service.ports.postgresql }} + targetPort: tcp-postgresql + {{- if not (empty $.Values.externalAccess.service.nodePorts.postgresql) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.postgresql $i }} + {{- else }} + nodePort: null + {{- end }} + - name: tcp-intersrv + port: {{ $.Values.externalAccess.service.ports.interserver }} + targetPort: tcp-intersrv + {{- if not (empty $.Values.externalAccess.service.nodePorts.interserver) }} + nodePort: {{ index $.Values.externalAccess.service.nodePorts.interserver $i }} + {{- else }} + nodePort: null + {{- end }} + {{- if $.Values.externalAccess.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" $.Values.externalAccess.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.podLabels $.Values.commonLabels ) "context" $ ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + statefulset.kubernetes.io/pod-name: {{ $targetPod }} +--- +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/clickhouse/templates/service-headless.yaml b/charts/clickhouse/templates/service-headless.yaml new file mode 100644 index 00000000..f989841b --- /dev/null +++ b/charts/clickhouse/templates/service-headless.yaml @@ -0,0 +1,69 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "clickhouse.headlessServiceName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if or .Values.service.headless.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.service.headless.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: http + targetPort: http + port: {{ .Values.service.ports.http }} + protocol: TCP + - name: tcp + targetPort: tcp + port: {{ .Values.service.ports.tcp }} + protocol: TCP + {{- if .Values.tls.enabled }} + - name: tcp-secure + targetPort: tcp-secure + port: {{ .Values.service.ports.tcpSecure }} + protocol: TCP + {{- end }} + {{- if .Values.keeper.enabled }} + - name: tcp-keeper + targetPort: tcp-keeper + port: {{ .Values.service.ports.keeper }} + protocol: TCP + - name: tcp-keeperinter + targetPort: tcp-keeperinter + port: {{ .Values.service.ports.keeperInter }} + protocol: TCP + {{- if .Values.tls.enabled }} + - name: tcp-keepertls + targetPort: tcp-keepertls + port: {{ .Values.service.ports.keeperSecure }} + protocol: TCP + {{- end }} + {{- end }} + - name: tcp-mysql + targetPort: tcp-mysql + port: {{ .Values.service.ports.mysql }} + protocol: TCP + - name: tcp-postgresql + targetPort: tcp-postgresql + port: {{ .Values.service.ports.postgresql }} + protocol: TCP + - name: http-intersrv + targetPort: http-intersrv + port: {{ .Values.service.ports.interserver }} + protocol: TCP + {{- if .Values.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.podLabels .Values.commonLabels ) "context" . ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse diff --git a/charts/clickhouse/templates/service.yaml b/charts/clickhouse/templates/service.yaml new file mode 100644 index 00000000..f54e2268 --- /dev/null +++ b/charts/clickhouse/templates/service.yaml @@ -0,0 +1,152 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if or .Values.service.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.service.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if .Values.service.sessionAffinity }} + sessionAffinity: {{ .Values.service.sessionAffinity }} + {{- end }} + {{- if .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + ports: + - name: http + targetPort: http + port: {{ .Values.service.ports.http }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.http)) }} + nodePort: {{ .Values.service.nodePorts.http }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.tls.enabled }} + - name: https + targetPort: https + port: {{ .Values.service.ports.https }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.https)) }} + nodePort: {{ .Values.service.nodePorts.https }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- end }} + - name: tcp + targetPort: tcp + port: {{ .Values.service.ports.tcp }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcp)) }} + nodePort: {{ .Values.service.nodePorts.tcp }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.tls.enabled }} + - name: tcp-secure + targetPort: tcp-secure + port: {{ .Values.service.ports.tcpSecure }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcpSecure)) }} + nodePort: {{ .Values.service.nodePorts.tcpSecure }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- end }} + {{- if .Values.keeper.enabled }} + - name: tcp-keeper + targetPort: tcp-keeper + port: {{ .Values.service.ports.keeper }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcp)) }} + nodePort: {{ .Values.service.nodePorts.keeper }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + - name: tcp-keeperinter + targetPort: tcp-keeperinter + port: {{ .Values.service.ports.keeperInter }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcp)) }} + nodePort: {{ .Values.service.nodePorts.keeperInter }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.tls.enabled }} + - name: tcp-keepertls + targetPort: tcp-keepertls + port: {{ .Values.service.ports.keeperSecure }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcpSecure)) }} + nodePort: {{ .Values.service.nodePorts.keeperSecure }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- end }} + {{- end }} + - name: tcp-mysql + targetPort: tcp-mysql + port: {{ .Values.service.ports.mysql }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.mysql)) }} + nodePort: {{ .Values.service.nodePorts.mysql }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + - name: tcp-postgresql + targetPort: tcp-postgresql + port: {{ .Values.service.ports.postgresql }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.postgresql)) }} + nodePort: {{ .Values.service.nodePorts.postgresql }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + - name: http-intersrv + targetPort: http-intersrv + port: {{ .Values.service.ports.interserver }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.interserver)) }} + nodePort: {{ .Values.service.nodePorts.interserver }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.metrics.enabled }} + - name: http-metrics + targetPort: http-metrics + port: {{ .Values.service.ports.metrics }} + protocol: TCP + {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.metrics)) }} + nodePort: {{ .Values.service.nodePorts.metrics }} + {{- else if eq .Values.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- end }} + {{- if .Values.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.podLabels .Values.commonLabels ) "context" . ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse diff --git a/charts/clickhouse/templates/servicemonitor.yaml b/charts/clickhouse/templates/servicemonitor.yaml new file mode 100644 index 00000000..2148b375 --- /dev/null +++ b/charts/clickhouse/templates/servicemonitor.yaml @@ -0,0 +1,47 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.namespace | quote }} + {{- $labels := include "common.tplvalues.merge" ( dict "values" ( list .Values.metrics.serviceMonitor.labels .Values.commonLabels ) "context" . ) }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $labels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if or .Values.metrics.serviceMonitor.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.metrics.serviceMonitor.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel | quote }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} + {{- if .Values.metrics.serviceMonitor.selector }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} + {{- end }} + endpoints: + - port: http-metrics + path: "/metrics" + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.relabelings }} + relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 8 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ include "common.names.namespace" . | quote }} +{{- end }} diff --git a/charts/clickhouse/templates/start-scripts-secret.yaml b/charts/clickhouse/templates/start-scripts-secret.yaml new file mode 100644 index 00000000..c579f2e4 --- /dev/null +++ b/charts/clickhouse/templates/start-scripts-secret.yaml @@ -0,0 +1,19 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.startdbScripts (not .Values.startdbScriptsSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ printf "%s-start-scripts" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +stringData: +{{- include "common.tplvalues.render" (dict "value" .Values.startdbScripts "context" .) | nindent 2 }} +{{- end }} diff --git a/charts/clickhouse/templates/statefulset-clickhouse-replica.yaml b/charts/clickhouse/templates/statefulset-clickhouse-replica.yaml deleted file mode 100755 index 9b1db994..00000000 --- a/charts/clickhouse/templates/statefulset-clickhouse-replica.yaml +++ /dev/null @@ -1,184 +0,0 @@ -{{- if .Values.clickhouse.configmap.remote_servers.replica.backup.enabled }} -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "clickhouse.fullname" . }}-replica - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica - app.kubernetes.io/instance: {{ .Release.Name }}-replica - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.clickhouse.replicas }} - podManagementPolicy: {{ .Values.clickhouse.podManagementPolicy }} - updateStrategy: - type: {{ .Values.clickhouse.updateStrategy }} - {{- if (eq "Recreate" .Values.clickhouse.updateStrategy) }} - rollingUpdate: null - {{- else if .Values.clickhouse.rollingUpdatePartition }} - rollingUpdate: - partition: {{ .Values.clickhouse.rollingUpdatePartition }} - {{- end }} - serviceName: {{ include "clickhouse.fullname" . }}-replica-headless - selector: - matchLabels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica - app.kubernetes.io/instance: {{ .Release.Name }}-replica - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica - app.kubernetes.io/instance: {{ .Release.Name }}-replica - spec: - {{- if .Values.affinity }} - affinity: -{{ toYaml .Values.affinity | indent 8 }} - {{- end }} - {{- if .Values.clickhouse.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.clickhouse.imagePullSecrets }} - - name: {{ . | quote }} - {{- end }} - {{- end }} - initContainers: - - name: init - image: busybox:1.31.0 - imagePullPolicy: IfNotPresent - args: - - /bin/sh - - -c - - | - mkdir -p /etc/clickhouse-server/metrica.d - containers: - - name: {{ include "clickhouse.fullname" . }}-replica - image: {{ .Values.clickhouse.image }}:{{ .Values.clickhouse.imageVersion }} - imagePullPolicy: {{ .Values.clickhouse.imagePullPolicy }} - ports: - - name: http-port - containerPort: {{ .Values.clickhouse.http_port | default "8123" }} - - name: tcp-port - containerPort: {{ .Values.clickhouse.tcp_port | default "9000" }} - - name: inter-http-port - containerPort: {{ .Values.clickhouse.interserver_http_port | default "9009" }} - {{- if .Values.clickhouse.livenessProbe.enabled }} - livenessProbe: - tcpSocket: - port: {{ .Values.clickhouse.tcp_port }} - initialDelaySeconds: {{ .Values.clickhouse.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.clickhouse.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.clickhouse.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.clickhouse.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.clickhouse.livenessProbe.successThreshold }} - {{- end }} - {{- if .Values.clickhouse.readinessProbe.enabled }} - readinessProbe: - tcpSocket: - port: {{ .Values.clickhouse.tcp_port }} - initialDelaySeconds: {{ .Values.clickhouse.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.clickhouse.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.clickhouse.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.clickhouse.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.clickhouse.readinessProbe.successThreshold }} - {{- end }} - volumeMounts: - - name: {{ include "clickhouse.fullname" . }}-replica-data - mountPath: {{ include "clickhouse.fullpath" . }} - - name: {{ include "clickhouse.fullname" . }}-replica-logs - mountPath: {{ include "clickhouse.logpath" . }} - - name: {{ include "clickhouse.fullname" . }}-config - mountPath: /etc/clickhouse-server/config.d - - name: {{ include "clickhouse.fullname" . }}-metrica - mountPath: /etc/clickhouse-server/metrica.d - - name: {{ include "clickhouse.fullname" . }}-users - mountPath: /etc/clickhouse-server/users.d - securityContext: - privileged: true - runAsUser: 0 - {{- if .Values.clickhouse.imagePullSecrets }} - imagePullSecrets: - - name: {{ .Values.clickhouse.imagePullSecrets }} - {{- end }} - {{- if .Values.clickhouse.nodeSelector }} - nodeSelector: -{{ toYaml .Values.clickhouse.nodeSelector | indent 8 }} - {{- end }} - volumes: - - name: {{ include "clickhouse.fullname" . }}-replica-data - {{- if .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.enabled }} - persistentVolumeClaim: - claimName: {{ include "clickhouse.fullname" . }}-replica-data - {{- else }} - emptyDir: {} - {{- end }} - - name: {{ include "clickhouse.fullname" . }}-replica-logs - {{- if .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.enabled }} - persistentVolumeClaim: - claimName: {{ include "clickhouse.fullname" . }}-replica-logs - {{- else }} - emptyDir: {} - {{- end }} - {{- if .Values.clickhouse.configmap.enabled }} - - name: {{ include "clickhouse.fullname" . }}-config - configMap: - name: {{ include "clickhouse.fullname" . }}-config - items: - - key: config.xml - path: config.xml - - name: {{ include "clickhouse.fullname" . }}-metrica - configMap: - name: {{ include "clickhouse.fullname" . }}-metrica - items: - - key: metrica.xml - path: metrica.xml - - name: {{ include "clickhouse.fullname" . }}-users - configMap: - name: {{ include "clickhouse.fullname" . }}-users - items: - - key: users.xml - path: users.xml - {{- end }} -{{- if .Values.clickhouse.persistentVolumeClaim.enabled }} - volumeClaimTemplates: -{{- if .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.enabled }} - - metadata: - name: {{ include "clickhouse.fullname" . }}-replica-data - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica-data - app.kubernetes.io/instance: {{ .Release.Name }}-replica-data - app.kubernetes.io/managed-by: {{ .Release.Service }} - spec: - accessModes: - {{- range .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.accessModes }} - - {{ . | quote }} - {{- end }} - {{- if (eq "-" .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.storageClassName) }} - storageClassName: "" - {{- else }} - storageClassName: {{ .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.storageClassName }} - {{- end }} - resources: - requests: - storage: {{ .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.storage }} -{{- end }} -{{- if .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.enabled }} - - metadata: - name: {{ include "clickhouse.fullname" . }}-replica-logs - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica-logs - app.kubernetes.io/instance: {{ .Release.Name }}-replica-logs - app.kubernetes.io/managed-by: {{ .Release.Service }} - spec: - accessModes: - {{- range .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.accessModes }} - - {{ . | quote }} - {{- end }} - {{- if (eq "-" .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.storageClassName) }} - storageClassName: "" - {{- else }} - storageClassName: {{ .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.storageClassName }} - {{- end }} - resources: - requests: - storage: {{ .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.storage }} -{{- end }} -{{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/statefulset-clickhouse.yaml b/charts/clickhouse/templates/statefulset-clickhouse.yaml deleted file mode 100755 index ec871274..00000000 --- a/charts/clickhouse/templates/statefulset-clickhouse.yaml +++ /dev/null @@ -1,182 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "clickhouse.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.clickhouse.replicas }} - podManagementPolicy: {{ .Values.clickhouse.podManagementPolicy }} - updateStrategy: - type: {{ .Values.clickhouse.updateStrategy }} - {{- if (eq "Recreate" .Values.clickhouse.updateStrategy) }} - rollingUpdate: null - {{- else if .Values.clickhouse.rollingUpdatePartition }} - rollingUpdate: - partition: {{ .Values.clickhouse.rollingUpdatePartition }} - {{- end }} - serviceName: {{ include "clickhouse.fullname" . }}-headless - selector: - matchLabels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - {{- if .Values.affinity }} - affinity: -{{ toYaml .Values.affinity | indent 8 }} - {{- end }} - {{- if .Values.clickhouse.imagePullSecrets }} - imagePullSecrets: - {{- range .Values.clickhouse.imagePullSecrets }} - - name: {{ . | quote }} - {{- end }} - {{- end }} - initContainers: - - name: init - image: busybox:1.31.0 - imagePullPolicy: IfNotPresent - args: - - /bin/sh - - -c - - | - mkdir -p /etc/clickhouse-server/metrica.d - containers: - - name: {{ include "clickhouse.fullname" . }} - image: {{ .Values.clickhouse.image }}:{{ .Values.clickhouse.imageVersion }} - imagePullPolicy: {{ .Values.clickhouse.imagePullPolicy }} - ports: - - name: http-port - containerPort: {{ .Values.clickhouse.http_port | default "8123" }} - - name: tcp-port - containerPort: {{ .Values.clickhouse.tcp_port | default "9000" }} - - name: inter-http-port - containerPort: {{ .Values.clickhouse.interserver_http_port | default "9009" }} - {{- if .Values.clickhouse.livenessProbe.enabled }} - livenessProbe: - tcpSocket: - port: {{ .Values.clickhouse.tcp_port }} - initialDelaySeconds: {{ .Values.clickhouse.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.clickhouse.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.clickhouse.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.clickhouse.livenessProbe.failureThreshold }} - successThreshold: {{ .Values.clickhouse.livenessProbe.successThreshold }} - {{- end }} - {{- if .Values.clickhouse.readinessProbe.enabled }} - readinessProbe: - tcpSocket: - port: {{ .Values.clickhouse.tcp_port }} - initialDelaySeconds: {{ .Values.clickhouse.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.clickhouse.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.clickhouse.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.clickhouse.readinessProbe.failureThreshold }} - successThreshold: {{ .Values.clickhouse.readinessProbe.successThreshold }} - {{- end }} - volumeMounts: - - name: {{ include "clickhouse.fullname" . }}-data - mountPath: {{ include "clickhouse.fullpath" . }} - - name: {{ include "clickhouse.fullname" . }}-logs - mountPath: {{ include "clickhouse.logpath" . }} - - name: {{ include "clickhouse.fullname" . }}-config - mountPath: /etc/clickhouse-server/config.d - - name: {{ include "clickhouse.fullname" . }}-metrica - mountPath: /etc/clickhouse-server/metrica.d - - name: {{ include "clickhouse.fullname" . }}-users - mountPath: /etc/clickhouse-server/users.d - securityContext: - privileged: true - runAsUser: 0 - {{- if .Values.clickhouse.imagePullSecrets }} - imagePullSecrets: - - name: {{ .Values.clickhouse.imagePullSecrets }} - {{- end }} - {{- if .Values.clickhouse.nodeSelector }} - nodeSelector: -{{ toYaml .Values.clickhouse.nodeSelector | indent 8 }} - {{- end }} - volumes: - - name: {{ include "clickhouse.fullname" . }}-data - {{- if .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.enabled }} - persistentVolumeClaim: - claimName: {{ include "clickhouse.fullname" . }}-data - {{- else }} - emptyDir: {} - {{- end }} - - name: {{ include "clickhouse.fullname" . }}-logs - {{- if .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.enabled }} - persistentVolumeClaim: - claimName: {{ include "clickhouse.fullname" . }}-logs - {{- else }} - emptyDir: {} - {{- end }} - {{- if .Values.clickhouse.configmap.enabled }} - - name: {{ include "clickhouse.fullname" . }}-config - configMap: - name: {{ include "clickhouse.fullname" . }}-config - items: - - key: config.xml - path: config.xml - - name: {{ include "clickhouse.fullname" . }}-metrica - configMap: - name: {{ include "clickhouse.fullname" . }}-metrica - items: - - key: metrica.xml - path: metrica.xml - - name: {{ include "clickhouse.fullname" . }}-users - configMap: - name: {{ include "clickhouse.fullname" . }}-users - items: - - key: users.xml - path: users.xml - {{- end }} -{{- if .Values.clickhouse.persistentVolumeClaim.enabled }} - volumeClaimTemplates: -{{- if .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.enabled }} - - metadata: - name: {{ include "clickhouse.fullname" . }}-data - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-data - app.kubernetes.io/instance: {{ .Release.Name }}-data - app.kubernetes.io/managed-by: {{ .Release.Service }} - spec: - accessModes: - {{- range .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.accessModes }} - - {{ . | quote }} - {{- end }} - {{- if (eq "-" .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.storageClassName) }} - storageClassName: "" - {{- else }} - storageClassName: {{ .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.storageClassName | quote }} - {{- end }} - resources: - requests: - storage: {{ .Values.clickhouse.persistentVolumeClaim.dataPersistentVolume.storage | quote }} -{{- end }} -{{- if .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.enabled }} - - metadata: - name: {{ include "clickhouse.fullname" . }}-logs - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-logs - app.kubernetes.io/instance: {{ .Release.Name }}-logs - app.kubernetes.io/managed-by: {{ .Release.Service }} - spec: - accessModes: - {{- range .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.accessModes }} - - {{ . | quote }} - {{- end }} - {{- if (eq "-" .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.storageClassName) }} - storageClassName: "" - {{- else }} - storageClassName: {{ .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.storageClassName | quote }} - {{- end }} - resources: - requests: - storage: {{ .Values.clickhouse.persistentVolumeClaim.logsPersistentVolume.storage | quote }} -{{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/statefulset.yaml b/charts/clickhouse/templates/statefulset.yaml new file mode 100644 index 00000000..13d526ca --- /dev/null +++ b/charts/clickhouse/templates/statefulset.yaml @@ -0,0 +1,425 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- $shards := .Values.shards | int }} +{{- range $i, $e := until $shards }} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" $ }} +kind: StatefulSet +metadata: + name: {{ printf "%s-shard%d" (include "common.names.fullname" $ ) $i }} + namespace: {{ include "common.names.namespace" $ | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: clickhouse + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: {{ $.Values.replicaCount }} + podManagementPolicy: {{ $.Values.podManagementPolicy | quote }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.podLabels $.Values.commonLabels ) "context" $ ) }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: clickhouse + serviceName: {{ printf "%s-headless" (include "common.names.fullname" $) }} + {{- if $.Values.updateStrategy }} + updateStrategy: {{- toYaml $.Values.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") $ | sha256sum }} + checksum/config-extra: {{ include (print $.Template.BasePath "/configmap-extra.yaml") $ | sha256sum }} + checksum/config-users-extra: {{ include (print $.Template.BasePath "/configmap-users-extra.yaml") $ | sha256sum }} + {{- if $.Values.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $.Values.podAnnotations "context" $) | nindent 8 }} + {{- end }} + {{- if and $.Values.metrics.enabled $.Values.metrics.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $.Values.metrics.podAnnotations "context" $) | nindent 8 }} + {{- end }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $podLabels "context" $ ) | nindent 8 }} + app.kubernetes.io/component: clickhouse + shard: {{ $i | quote }} + spec: + serviceAccountName: {{ template "clickhouse.serviceAccountName" $ }} + {{- include "clickhouse.imagePullSecrets" $ | nindent 6 }} + {{- if $.Values.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" $.Values.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if $.Values.affinity }} + affinity: {{- include "common.tplvalues.render" ( dict "value" $.Values.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" $.Values.podAffinityPreset "component" "clickhouse" "customLabels" $podLabels "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" $.Values.podAntiAffinityPreset "component" "clickhouse" "customLabels" $podLabels "extraPodAffinityTerms" (ternary (list (dict "extraMatchLabels" (dict "shard" $i) "topologyKey" "topology.kubernetes.io/zone")) (list) $.Values.distributeReplicasByZone) "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" $.Values.nodeAffinityPreset.type "key" $.Values.nodeAffinityPreset.key "values" $.Values.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if $.Values.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" ( dict "value" $.Values.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if $.Values.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" $.Values.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if $.Values.priorityClassName }} + priorityClassName: {{ $.Values.priorityClassName | quote }} + {{- end }} + {{- if $.Values.schedulerName }} + schedulerName: {{ $.Values.schedulerName | quote }} + {{- end }} + {{- if $.Values.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" $.Values.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if $.Values.podSecurityContext.enabled }} + securityContext: {{- omit $.Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if $.Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ $.Values.terminationGracePeriodSeconds }} + {{- end }} + initContainers: + {{- if and $.Values.tls.enabled (not $.Values.volumePermissions.enabled) }} + - name: copy-certs + image: {{ include "clickhouse.volumePermissions.image" $ }} + imagePullPolicy: {{ $.Values.volumePermissions.image.pullPolicy | quote }} + {{- if $.Values.resources }} + resources: {{- toYaml $.Values.resources | nindent 12 }} + {{- end }} + {{- if $.Values.containerSecurityContext.enabled }} + # We don't require a privileged container in this case + securityContext: {{- omit $.Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -ec + - | + cp -L /tmp/certs/* /opt/bitnami/clickhouse/certs/ + chmod 600 {{ include "clickhouse.tlsCertKey" $ }} + volumeMounts: + - name: raw-certificates + mountPath: /tmp/certs + - name: clickhouse-certificates + mountPath: /opt/bitnami/clickhouse/certs + {{- else if and $.Values.volumePermissions.enabled $.Values.persistence.enabled }} + - name: volume-permissions + image: {{ include "clickhouse.volumePermissions.image" $ }} + imagePullPolicy: {{ $.Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/sh + - -ec + - | + mkdir -p /bitnami/clickhouse/data + chmod 700 /bitnami/clickhouse/data + {{- if $.Values.keeper.enabled }} + mkdir -p /bitnami/clickhouse/keeper + chmod 700 /bitnami/clickhouse/keeper + {{- end }} + chown {{ $.Values.containerSecurityContext.runAsUser }}:{{ $.Values.podSecurityContext.fsGroup }} /bitnami/clickhouse + find /bitnami/clickhouse -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | \ + xargs -r chown -R {{ $.Values.containerSecurityContext.runAsUser }}:{{ $.Values.podSecurityContext.fsGroup }} + {{- if $.Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/clickhouse/certs/ + {{- if eq ( toString ( $.Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/clickhouse/certs/ + {{- else }} + chown -R {{ $.Values.containerSecurityContext.runAsUser }}:{{ $.Values.podSecurityContext.fsGroup }} /opt/bitnami/clickhouse/certs/ + {{- end }} + chmod 600 {{ include "clickhouse.tlsCertKey" $ }} + {{- end }} + securityContext: {{- include "common.tplvalues.render" (dict "value" $.Values.volumePermissions.containerSecurityContext "context" $) | nindent 12 }} + {{- if $.Values.volumePermissions.resources }} + resources: {{- toYaml $.Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/clickhouse + {{- if $.Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: clickhouse-certificates + mountPath: /opt/bitnami/clickhouse/certs + {{- end }} + {{- end }} + {{- if $.Values.initContainers }} + {{- include "common.tplvalues.render" (dict "value" $.Values.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: clickhouse + image: {{ template "clickhouse.image" $ }} + imagePullPolicy: {{ $.Values.image.pullPolicy }} + {{- if $.Values.containerSecurityContext.enabled }} + securityContext: {{- omit $.Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if $.Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" $.Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if $.Values.command }} + command: {{- include "common.tplvalues.render" (dict "value" $.Values.command "context" $) | nindent 12 }} + {{- end }} + {{- if $.Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" $.Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if $.Values.args }} + args: {{- include "common.tplvalues.render" (dict "value" $.Values.args "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or $.Values.image.debug $.Values.diagnosticMode.enabled) | quote }} + - name: CLICKHOUSE_HTTP_PORT + value: {{ $.Values.containerPorts.http | quote }} + - name: CLICKHOUSE_TCP_PORT + value: {{ $.Values.containerPorts.tcp | quote }} + - name: CLICKHOUSE_MYSQL_PORT + value: {{ $.Values.containerPorts.mysql | quote }} + - name: CLICKHOUSE_POSTGRESQL_PORT + value: {{ $.Values.containerPorts.postgresql | quote }} + - name: CLICKHOUSE_INTERSERVER_HTTP_PORT + value: {{ $.Values.containerPorts.interserver | quote }} + {{- if $.Values.tls.enabled }} + - name: CLICKHOUSE_TCP_SECURE_PORT + value: {{ $.Values.containerPorts.tcpSecure | quote }} + - name: CLICKHOUSE_HTTPS_PORT + value: {{ $.Values.containerPorts.https | quote }} + {{- end }} + {{- if $.Values.keeper.enabled }} + - name: CLICKHOUSE_KEEPER_PORT + value: {{ $.Values.containerPorts.keeper | quote }} + - name: CLICKHOUSE_KEEPER_INTER_PORT + value: {{ $.Values.containerPorts.keeperInter | quote }} + {{- if $.Values.tls.enabled }} + - name: CLICKHOUSE_KEEPER_SECURE_PORT + value: {{ $.Values.containerPorts.keeperSecure | quote }} + {{- end }} + {{- end }} + {{- if $.Values.metrics.enabled }} + - name: CLICKHOUSE_METRICS_PORT + value: {{ $.Values.containerPorts.metrics | quote }} + {{- end }} + - name: CLICKHOUSE_SHARD_ID + value: {{ printf "shard%d" $i | quote }} + - name: CLICKHOUSE_REPLICA_ID + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ALLOW_EMPTY_PASSWORD + value: "yes" + {{- if $.Values.tls.enabled }} + - name: CLICKHOUSE_TLS_CERT_FILE + value: {{ include "clickhouse.tlsCert" $ | quote}} + - name: CLICKHOUSE_TLS_KEY_FILE + value: {{ include "clickhouse.tlsCertKey" $ | quote }} + - name: CLICKHOUSE_TLS_CA_FILE + value: {{ include "clickhouse.tlsCACert" $ | quote }} + {{- end }} + {{- if $.Values.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" $.Values.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if $.Values.keeper.enabled }} + {{- $replicas := $.Values.replicaCount | int }} + {{- range $j, $r := until $replicas }} + - name: {{ printf "KEEPER_NODE_%d" $j }} + value: {{ printf "%s-shard%d-%d.%s.%s.svc.%s" (include "common.names.fullname" $ ) $i $j (include "clickhouse.headlessServiceName" $) (include "common.names.namespace" $) $.Values.clusterDomain }} + {{- end }} + {{- else if $.Values.zookeeper.enabled }} + {{- $replicas := $.Values.zookeeper.replicaCount | int }} + {{- range $j, $r := until $replicas }} + - name: {{ printf "KEEPER_NODE_%d" $j }} + value: {{ printf "%s-%d.%s.%s.svc.%s" (include "clickhouse.zookeeper.fullname" $ ) $j (include "clickhouse.zookeeper.headlessServiceName" $) (include "common.names.namespace" $) $.Values.clusterDomain }} + {{- end }} + {{- end }} + envFrom: + {{- if $.Values.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" $.Values.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if $.Values.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" $.Values.extraEnvVarsSecret "context" $) }} + {{- end }} + {{- if $.Values.resources }} + resources: {{- toYaml $.Values.resources | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: {{ $.Values.containerPorts.http }} + - name: tcp + containerPort: {{ $.Values.containerPorts.tcp }} + {{- if $.Values.tls.enabled }} + - name: https + containerPort: {{ $.Values.containerPorts.https }} + - name: tcp-secure + containerPort: {{ $.Values.containerPorts.tcpSecure }} + {{- end }} + {{- if $.Values.keeper.enabled }} + - name: tcp-keeper + containerPort: {{ $.Values.containerPorts.keeper }} + - name: tcp-keeperinter + containerPort: {{ $.Values.containerPorts.keeperInter }} + {{- if $.Values.tls.enabled }} + - name: tcp-keepertls + containerPort: {{ $.Values.containerPorts.keeperSecure }} + {{- end }} + {{- end }} + - name: tcp-postgresql + containerPort: {{ $.Values.containerPorts.postgresql }} + - name: tcp-mysql + containerPort: {{ $.Values.containerPorts.mysql }} + - name: http-intersrv + containerPort: {{ $.Values.containerPorts.interserver }} + {{- if $.Values.metrics.enabled }} + - name: http-metrics + containerPort: {{ $.Values.containerPorts.metrics }} + {{- end }} + {{- if not $.Values.diagnosticMode.enabled }} + {{- if $.Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" $.Values.customLivenessProbe "context" $) | nindent 12 }} + {{- else if $.Values.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit $.Values.livenessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: /ping + port: http + {{- end }} + {{- if $.Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" $.Values.customReadinessProbe "context" $) | nindent 12 }} + {{- else if $.Values.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit $.Values.readinessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: /ping + port: http + {{- end }} + {{- if $.Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" $.Values.customStartupProbe "context" $) | nindent 12 }} + {{- else if $.Values.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit $.Values.startupProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: /ping + port: http + {{- end }} + {{- end }} + {{- if $.Values.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" $.Values.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + - name: scripts + mountPath: /scripts/setup.sh + subPath: setup.sh + - name: data + mountPath: /bitnami/clickhouse + - name: config + mountPath: /bitnami/clickhouse/etc/conf.d/default + {{- if or $.Values.extraOverridesConfigmap $.Values.extraOverrides }} + - name: extra-config + mountPath: /bitnami/clickhouse/etc/conf.d/extra-configmap + {{- end }} + {{- if or $.Values.usersExtraOverridesConfigmap $.Values.usersExtraOverrides }} + - name: users-extra-config + mountPath: /bitnami/clickhouse/etc/users.d/users-extra-configmap + {{- end }} + {{- if $.Values.extraOverridesSecret }} + - name: extra-secret + mountPath: /bitnami/clickhouse/etc/conf.d/extra-secret + {{- end }} + {{- if $.Values.usersExtraOverridesSecret }} + - name: users-extra-secret + mountPath: /bitnami/clickhouse/etc/users.d/users-extra-secret + {{- end }} + {{- if $.Values.tls.enabled }} + - name: clickhouse-certificates + mountPath: /bitnami/clickhouse/certs + {{- end }} + {{- if or $.Values.initdbScriptsSecret $.Values.initdbScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d + {{- end }} + {{- if or $.Values.startdbScriptsSecret $.Values.startdbScripts }} + - name: custom-start-scripts + mountPath: /docker-entrypoint-startdb.d + {{- end }} + {{- if $.Values.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" $.Values.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if $.Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + - name: scripts + configMap: + name: {{ printf "%s-scripts" (include "common.names.fullname" $) }} + defaultMode: 0755 + - name: config + configMap: + name: {{ template "clickhouse.configmapName" $ }} + {{- if or $.Values.initdbScriptsSecret $.Values.initdbScripts }} + - name: custom-init-scripts + secret: + secretName: {{ include "clickhouse.initdbScriptsSecret" $ }} + {{- end }} + {{- if or $.Values.startdbScriptsSecret $.Values.startdbScripts }} + - name: custom-start-scripts + secret: + secretName: {{ include "clickhouse.startdbScriptsSecret" $ }} + {{- end }} + {{- if or $.Values.extraOverridesConfigmap $.Values.extraOverrides }} + - name: extra-config + configMap: + name: {{ template "clickhouse.extraConfigmapName" $ }} + {{- end }} + {{- if or $.Values.usersExtraOverridesConfigmap $.Values.usersExtraOverrides }} + - name: users-extra-config + configMap: + name: {{ template "clickhouse.usersExtraConfigmapName" $ }} + {{- end }} + {{- if $.Values.extraOverridesSecret }} + - name: extra-secret + secret: + secretName: {{ $.Values.extraOverridesSecret }} + {{- end }} + {{- if $.Values.usersExtraOverridesSecret }} + - name: users-extra-secret + secret: + secretName: {{ $.Values.usersExtraOverridesSecret }} + {{- end }} + {{- if not $.Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if $.Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ tpl $.Values.persistence.existingClaim $ }} + {{- end }} + {{- if $.Values.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ include "clickhouse.tlsSecretName" $ }} + - name: clickhouse-certificates + emptyDir: {} + {{- end }} + {{- if $.Values.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" $.Values.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if and $.Values.persistence.enabled (not $.Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + {{- if or $.Values.persistence.annotations $.Values.commonAnnotations }} + {{- $claimAnnotations := include "common.tplvalues.merge" ( dict "values" ( list $.Values.persistence.annotations $.Values.commonLabels ) "context" $ ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $claimAnnotations "context" $ ) | nindent 10 }} + {{- end }} + {{- $claimLabels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.persistence.labels $.Values.commonLabels ) "context" $ ) }} + labels: {{- include "common.labels.matchLabels" ( dict "customLabels" $claimLabels "context" $ ) | nindent 10 }} + app.kubernetes.io/component: clickhouse + spec: + accessModes: + {{- range $.Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ $.Values.persistence.size | quote }} + {{- if $.Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" $.Values.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{- if $.Values.persistence.dataSource }} + dataSource: {{- include "common.tplvalues.render" (dict "value" $.Values.persistence.dataSource "context" $) | nindent 10 }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" $.Values.persistence "global" $.Values.global) | nindent 8 }} + {{- end }} +--- +{{- end }} diff --git a/charts/clickhouse/templates/svc-clickhouse-headless.yaml b/charts/clickhouse/templates/svc-clickhouse-headless.yaml deleted file mode 100755 index 980c2773..00000000 --- a/charts/clickhouse/templates/svc-clickhouse-headless.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "clickhouse.fullname" . }}-headless - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-headless - app.kubernetes.io/instance: {{ .Release.Name }}-headless - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - clusterIP: "None" - ports: - - port: {{ .Values.clickhouse.tcp_port }} - targetPort: tcp-port - protocol: TCP - name: tcp-port - - port: {{ .Values.clickhouse.http_port }} - targetPort: http-port - protocol: TCP - name: http-port - - port: {{ .Values.clickhouse.interserver_http_port }} - targetPort: inter-http-port - protocol: TCP - name: inter-http-port - selector: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/charts/clickhouse/templates/svc-clickhouse-replica-headless.yaml b/charts/clickhouse/templates/svc-clickhouse-replica-headless.yaml deleted file mode 100755 index b26448d9..00000000 --- a/charts/clickhouse/templates/svc-clickhouse-replica-headless.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "clickhouse.fullname" . }}-replica-headless - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica-headless - app.kubernetes.io/instance: {{ .Release.Name }}-replica-headless - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - clusterIP: "None" - ports: - - port: {{ .Values.clickhouse.tcp_port }} - targetPort: tcp-port - protocol: TCP - name: tcp-port - - port: {{ .Values.clickhouse.http_port }} - targetPort: http-port - protocol: TCP - name: http-port - - port: {{ .Values.clickhouse.interserver_http_port }} - targetPort: inter-http-port - protocol: TCP - name: inter-http-port - selector: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica - app.kubernetes.io/instance: {{ .Release.Name }}-replica diff --git a/charts/clickhouse/templates/svc-clickhouse-replica.yaml b/charts/clickhouse/templates/svc-clickhouse-replica.yaml deleted file mode 100755 index b3fd7eef..00000000 --- a/charts/clickhouse/templates/svc-clickhouse-replica.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "clickhouse.fullname" . }}-replica - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica - app.kubernetes.io/instance: {{ .Release.Name }}-replica - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - ports: - - port: {{ .Values.clickhouse.tcp_port }} - targetPort: tcp-port - protocol: TCP - name: tcp-port - - port: {{ .Values.clickhouse.http_port }} - targetPort: http-port - protocol: TCP - name: http-port - - port: {{ .Values.clickhouse.interserver_http_port }} - targetPort: inter-http-port - protocol: TCP - name: inter-http-port - selector: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-replica - app.kubernetes.io/instance: {{ .Release.Name }}-replica diff --git a/charts/clickhouse/templates/svc-clickhouse.yaml b/charts/clickhouse/templates/svc-clickhouse.yaml deleted file mode 100755 index b73c81a5..00000000 --- a/charts/clickhouse/templates/svc-clickhouse.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "clickhouse.fullname" . }} - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - ports: - - port: {{ .Values.clickhouse.tcp_port }} - targetPort: tcp-port - protocol: TCP - name: tcp-port - - port: {{ .Values.clickhouse.http_port }} - targetPort: http-port - protocol: TCP - name: http-port - - port: {{ .Values.clickhouse.interserver_http_port }} - targetPort: inter-http-port - protocol: TCP - name: inter-http-port - selector: - app.kubernetes.io/name: {{ include "clickhouse.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/charts/clickhouse/templates/svc-tabix.yaml b/charts/clickhouse/templates/svc-tabix.yaml deleted file mode 100755 index 56df5caa..00000000 --- a/charts/clickhouse/templates/svc-tabix.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- if .Values.tabix.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "clickhouse.fullname" . }}-tabix - labels: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-tabix - app.kubernetes.io/instance: {{ .Release.Name }}-tabix - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - ports: - - port: 80 - targetPort: http - protocol: TCP - name: http - selector: - app.kubernetes.io/name: {{ include "clickhouse.name" . }}-tabix - app.kubernetes.io/instance: {{ .Release.Name }}-tabix -{{- end }} diff --git a/charts/clickhouse/templates/tls-secret.yaml b/charts/clickhouse/templates/tls-secret.yaml new file mode 100644 index 00000000..04b188e1 --- /dev/null +++ b/charts/clickhouse/templates/tls-secret.yaml @@ -0,0 +1,29 @@ +{{- /* +Copyright VMware, Inc. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if (include "clickhouse.createTlsSecret" . ) }} +{{- $secretName := printf "%s-crt" (include "common.names.fullname" .) }} +{{- $ca := genCA "clickhouse-ca" 365 }} +{{- $fullname := include "common.names.fullname" . }} +{{- $releaseNamespace := .Release.Namespace }} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $primaryHeadlessServiceName := printf "%s-headless" (include "common.names.fullname" .)}} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $fullname $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $fullname $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $primaryHeadlessServiceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $primaryHeadlessServiceName $releaseNamespace $clusterDomain) $fullname }} +{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} diff --git a/charts/clickhouse/values.yaml b/charts/clickhouse/values.yaml old mode 100755 new mode 100644 index 2989bd88..444f13fa --- a/charts/clickhouse/values.yaml +++ b/charts/clickhouse/values.yaml @@ -1,374 +1,1131 @@ -## Timezone -timezone: "Asia/Shanghai" - -## Cluster domain -clusterDomain: "cluster.local" - -## -## Clickhouse Node selectors and tolerations for pod assignment -## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector -## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature -## -# nodeSelector: {"beta.kubernetes.io/arch": "amd64"} -# tolerations: [] -## Clickhouse pod/node affinity/anti-affinity -## -#affinity: -# nodeAffinity: -# requiredDuringSchedulingIgnoredDuringExecution: -# nodeSelectorTerms: -# - matchExpressions: -# - key: "application/clickhouse" -# operator: In -# values: -# - "true" - -clickhouse: - ## StatefulSet controller supports relax its ordering guarantees while preserving its uniqueness and identity guarantees. There are two valid pod management policies: OrderedReady and Parallel - ## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#pod-management-policy - ## - podManagementPolicy: "Parallel" - - ## StatefulSet controller supports automated updates. There are two valid update strategies: RollingUpdate and OnDelete - ## ref: https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets - ## - updateStrategy: "RollingUpdate" - - ## Partition update strategy - ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions - ## - # rollingUpdatePartition: - - ## - ## The path to the directory containing data. - ## Default value: /var/lib/clickhouse - path: "/var/lib/clickhouse" - ## - ## The port for connecting to the server over HTTP - http_port: "8123" - ## - ## Port for communicating with clients over the TCP protocol. - tcp_port: "9000" - ## - ## Port for exchanging data between ClickHouse servers. - interserver_http_port: "9009" - ## - ## The instance number of Clickhouse - replicas: "3" - ## Clickhouse image configuration. - image: "clickhouse/clickhouse-server" - imageVersion: "23.6.2.18" - imagePullPolicy: "IfNotPresent" - #imagePullSecrets: - ## Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. - ## More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes - livenessProbe: - enabled: true - initialDelaySeconds: "30" - periodSeconds: "30" - timeoutSeconds: "5" - failureThreshold: "3" - successThreshold: "1" - ## Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. - ## More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes - readinessProbe: - enabled: true - initialDelaySeconds: "30" - periodSeconds: "30" - timeoutSeconds: "5" - failureThreshold: "3" - successThreshold: "1" - ## volumeClaimTemplates is a list of claims that pods are allowed to reference. - ## The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. - ## Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. - ## A claim in this list takes precedence over any volumes in the template, with the same name. - persistentVolumeClaim: - enabled: false - ## Clickhouse data volume - dataPersistentVolume: - enabled: false - accessModes: - - "ReadWriteOnce" - storageClassName: "-" - storage: "500Gi" - ## Clickhouse logs volume - logsPersistentVolume: - enabled: false - accessModes: - - "ReadWriteOnce" - storageClassName: "-" - storage: "50Gi" - ## - ## An API object that manages external access to the services in a cluster, typically HTTP. - ## Ingress can provide load balancing, SSL termination and name-based virtual hosting. - ingress: - enabled: false - # host: "clickhouse.domain.com" - # path: "/" - # tls: - # enabled: false - # hosts: - # - "clickhouse.domain.com" - # - "clickhouse.domain1.com" - # secretName: "clickhouse-secret" - ## - ## Clickhouse config.xml and metrica.xml - configmap: +# Copyright VMware, Inc. +# SPDX-License-Identifier: APACHE-2.0 + +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.storageClass Global StorageClass for Persistent Volume(s) +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + storageClass: "" + +## @section Common parameters +## + +## @param kubeVersion Override Kubernetes version +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.name +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname +## +fullnameOverride: "" +## @param namespaceOverride String to fully override common.names.namespace +## +namespaceOverride: "" +## @param commonLabels Labels to add to all deployed objects +## +commonLabels: {} +## @param commonAnnotations Annotations to add to all deployed objects +## +commonAnnotations: {} +## @param clusterDomain Kubernetes cluster domain name +## +clusterDomain: cluster.local +## @param extraDeploy Array of extra objects to deploy with the release +## +extraDeploy: [] + +## Enable diagnostic mode in the deployment +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the deployment + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the deployment + ## + args: + - infinity + +## @section ClickHouse Parameters +## + +## Bitnami ClickHouse image +## ref: https://hub.docker.com/r/bitnami/clickhouse/tags/ +## @param image.registry [default: REGISTRY_NAME] ClickHouse image registry +## @param image.repository [default: REPOSITORY_NAME/clickhouse] ClickHouse image repository +## @skip image.tag ClickHouse image tag (immutable tags are recommended) +## @param image.digest ClickHouse image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy ClickHouse image pull policy +## @param image.pullSecrets ClickHouse image pull secrets +## @param image.debug Enable ClickHouse image debug mode +## +image: + registry: docker.io + repository: bitnami/clickhouse + tag: 23.10.5-debian-11-r0 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Enable debug mode + ## + debug: false +## @param shards Number of ClickHouse shards to deploy +## +shards: 2 + +## @param replicaCount Number of ClickHouse replicas per shard to deploy +## if keeper enable, same as keeper count, keeper cluster by shards. +## +replicaCount: 3 + +## @param distributeReplicasByZone Schedules replicas of the same shard to different availability zones +## +distributeReplicasByZone: false +## @param containerPorts.http ClickHouse HTTP container port +## @param containerPorts.https ClickHouse HTTPS container port +## @param containerPorts.tcp ClickHouse TCP container port +## @param containerPorts.tcpSecure ClickHouse TCP (secure) container port +## @param containerPorts.keeper ClickHouse keeper TCP container port +## @param containerPorts.keeperSecure ClickHouse keeper TCP (secure) container port +## @param containerPorts.keeperInter ClickHouse keeper interserver TCP container port +## @param containerPorts.mysql ClickHouse MySQL container port +## @param containerPorts.postgresql ClickHouse PostgreSQL container port +## @param containerPorts.interserver ClickHouse Interserver container port +## @param containerPorts.metrics ClickHouse metrics container port +## +containerPorts: + http: 8123 + https: 8443 + tcp: 9000 + tcpSecure: 9440 + keeper: 2181 + keeperSecure: 3181 + keeperInter: 9444 + mysql: 9004 + postgresql: 9005 + interserver: 9009 + metrics: 8001 +## Configure extra options for ClickHouse containers' liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +## @param livenessProbe.enabled Enable livenessProbe on ClickHouse containers +## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe +## @param livenessProbe.periodSeconds Period seconds for livenessProbe +## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe +## @param livenessProbe.failureThreshold Failure threshold for livenessProbe +## @param livenessProbe.successThreshold Success threshold for livenessProbe +## +livenessProbe: + enabled: true + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 +## @param readinessProbe.enabled Enable readinessProbe on ClickHouse containers +## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe +## @param readinessProbe.periodSeconds Period seconds for readinessProbe +## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe +## @param readinessProbe.failureThreshold Failure threshold for readinessProbe +## @param readinessProbe.successThreshold Success threshold for readinessProbe +## +readinessProbe: + enabled: true + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 +## @param startupProbe.enabled Enable startupProbe on ClickHouse containers +## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe +## @param startupProbe.periodSeconds Period seconds for startupProbe +## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe +## @param startupProbe.failureThreshold Failure threshold for startupProbe +## @param startupProbe.successThreshold Success threshold for startupProbe +## +startupProbe: + enabled: false + failureThreshold: 3 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 +## @param customLivenessProbe Custom livenessProbe that overrides the default one +## +customLivenessProbe: {} +## @param customReadinessProbe Custom readinessProbe that overrides the default one +## +customReadinessProbe: {} +## @param customStartupProbe Custom startupProbe that overrides the default one +## +customStartupProbe: {} +## ClickHouse resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## @param resources.limits The resources limits for the ClickHouse containers +## @param resources.requests The requested resources for the ClickHouse containers +## +resources: + limits: {} + requests: {} +## Configure Pods Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## @param podSecurityContext.enabled Enabled ClickHouse pods' Security Context +## @param podSecurityContext.fsGroup Set ClickHouse pod's Security Context fsGroup +## If you are using Kubernetes 1.18, the following code needs to be commented out. +## +podSecurityContext: + enabled: true + fsGroup: 1001 +## Configure Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## @param containerSecurityContext.enabled Enable containers' Security Context +## @param containerSecurityContext.runAsUser Set containers' Security Context runAsUser +## @param containerSecurityContext.runAsNonRoot Set containers' Security Context runAsNonRoot +## @param containerSecurityContext.readOnlyRootFilesystem Set read only root file system pod's +## @param containerSecurityContext.privileged Set contraller container's Security Context privileged +## @param containerSecurityContext.allowPrivilegeEscalation Set contraller container's Security Context allowPrivilegeEscalation +## @param containerSecurityContext.capabilities.drop List of capabilities to be droppedn +## @param containerSecurityContext.seccompProfile.type Set container's Security Context seccomp profile +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: ["ALL"] + seccompProfile: + type: "RuntimeDefault" + +## Authentication +## @param auth.username ClickHouse Admin username +## @param auth.password ClickHouse Admin password +## @param auth.existingSecret Name of a secret containing the Admin password +## @param auth.existingSecretKey Name of the key inside the existing secret +## +auth: + username: "" + password: "" + existingSecret: "" + existingSecretKey: "" + +## @param logLevel Logging level +## +logLevel: information + +## @section ClickHouse keeper configuration parameters +## @param keeper.enabled Deploy ClickHouse keeper. Support is experimental. +## +keeper: + enabled: false + +## @param defaultConfigurationOverrides [string] Default configuration overrides (evaluated as a template) +## +defaultConfigurationOverrides: | + + + + + + {{ include "common.names.fullname" . }} + + + + {{ .Values.logLevel }} + + {{- if or (ne (int .Values.shards) 1) (ne (int .Values.replicaCount) 1)}} + + + + {{- $shards := $.Values.shards | int }} + {{- range $shard, $e := until $shards }} + + {{- $replicas := $.Values.replicaCount | int }} + {{- range $i, $_e := until $replicas }} + + {{ printf "%s-shard%d-%d.%s.%s.svc.%s" (include "common.names.fullname" $ ) $shard $i (include "clickhouse.headlessServiceName" $) (include "common.names.namespace" $) $.Values.clusterDomain }} + {{ $.Values.service.ports.tcp }} + + + + {{- end }} + + {{- end }} + + + {{- end }} + {{- if .Values.keeper.enabled }} + + + {{/*ClickHouse keeper configuration using the helm chart */}} + {{ $.Values.containerPorts.keeper }} + {{- if .Values.tls.enabled }} + {{ $.Values.containerPorts.keeperSecure }} + {{- end }} + + /bitnami/clickhouse/keeper/coordination/log + /bitnami/clickhouse/keeper/coordination/snapshots + + + 10000 + 30000 + trace + + + + {{- $nodes := .Values.replicaCount | int }} + {{- range $node, $e := until $nodes }} + + {{ $node | int }} + + {{ $.Values.service.ports.keeperInter }} + + {{- end }} + + + {{- end }} + {{- if or .Values.keeper.enabled .Values.zookeeper.enabled .Values.externalZookeeper.servers }} + + + {{- if or .Values.keeper.enabled }} + {{- $nodes := .Values.replicaCount | int }} + {{- range $node, $e := until $nodes }} + + + {{ $.Values.service.ports.keeper }} + + {{- end }} + {{- else if .Values.zookeeper.enabled }} + {{/* Zookeeper configuration using the helm chart */}} + {{- $nodes := .Values.zookeeper.replicaCount | int }} + {{- range $node, $e := until $nodes }} + + + {{ $.Values.zookeeper.service.ports.client }} + + {{- end }} + {{- else if .Values.externalZookeeper.servers }} + {{/* Zookeeper configuration using an external instance */}} + {{- range $node :=.Values.externalZookeeper.servers }} + + {{ $node }} + {{ $.Values.externalZookeeper.port }} + + {{- end }} + {{- end }} + + {{- end }} + {{- if .Values.tls.enabled }} + + + + + + {{- $certFileName := default "tls.crt" .Values.tls.certFilename }} + {{- $keyFileName := default "tls.key" .Values.tls.certKeyFilename }} + /bitnami/clickhouse/certs/{{$certFileName}} + /bitnami/clickhouse/certs/{{$keyFileName}} + none + true + sslv2,sslv3 + true + {{- if or .Values.tls.autoGenerated .Values.tls.certCAFilename }} + {{- $caFileName := default "ca.crt" .Values.tls.certCAFilename }} + /bitnami/clickhouse/certs/{{$caFileName}} + {{- else }} + true + {{- end }} + + + true + true + sslv2,sslv3 + true + none + + AcceptCertificateHandler + + + + {{- end }} + {{- if .Values.metrics.enabled }} + + + /metrics + + true + true + true + + {{- end }} + + +## @param existingOverridesConfigmap The name of an existing ConfigMap with your custom configuration for ClickHouse +## +existingOverridesConfigmap: "" + +## @param extraOverrides Extra configuration overrides (evaluated as a template) apart from the default +## +extraOverrides: "" + +## @param extraOverridesConfigmap The name of an existing ConfigMap with extra configuration for ClickHouse +## +extraOverridesConfigmap: "" + +## @param extraOverridesSecret The name of an existing ConfigMap with your custom configuration for ClickHouse +## +extraOverridesSecret: "" + +## @param usersExtraOverrides Users extra configuration overrides (evaluated as a template) apart from the default +## +usersExtraOverrides: "" + +## @param usersExtraOverridesConfigmap The name of an existing ConfigMap with users extra configuration for ClickHouse +## +usersExtraOverridesConfigmap: "" + +## @param usersExtraOverridesSecret The name of an existing ConfigMap with your custom users configuration for ClickHouse +## +usersExtraOverridesSecret: "" + +## @param initdbScripts Dictionary of initdb scripts +## Specify dictionary of scripts to be run at first boot +## Example: +## initdbScripts: +## my_init_script.sh: | +## #!/bin/bash +## echo "Do something." +## +initdbScripts: {} +## @param initdbScriptsSecret ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) +## +initdbScriptsSecret: "" + +## @param startdbScripts Dictionary of startdb scripts +## Specify dictionary of scripts to be run on every start +## Example: +## startdbScripts: +## my_start_script.sh: | +## #!/bin/bash +## echo "Do something." +## +startdbScripts: {} +## @param startdbScriptsSecret ConfigMap with the startdb scripts (Note: Overrides `startdbScripts`) +## +startdbScriptsSecret: "" + +## @param command Override default container command (useful when using custom images) +## +command: + - /scripts/setup.sh +## @param args Override default container args (useful when using custom images) +## +args: [] +## @param hostAliases ClickHouse pods host aliases +## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ +## +hostAliases: [] +## @param podLabels Extra labels for ClickHouse pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} +## @param podAnnotations Annotations for ClickHouse pods +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} +## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAffinityPreset: "" +## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## +podAntiAffinityPreset: soft +## Node affinity preset +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity +## +nodeAffinityPreset: + ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param nodeAffinityPreset.key Node label key to match. Ignored if `affinity` is set + ## + key: "" + ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] +## @param affinity Affinity for ClickHouse pods assignment +## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## NOTE: `podAffinityPreset`, `podAntiAffinityPreset`, and `nodeAffinityPreset` will be ignored when it's set +## +affinity: {} +## @param nodeSelector Node labels for ClickHouse pods assignment +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} +## @param tolerations Tolerations for ClickHouse pods assignment +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] +## @param updateStrategy.type ClickHouse statefulset strategy type +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +## +updateStrategy: + ## StrategyType + ## Can be set to RollingUpdate or OnDelete + ## + type: RollingUpdate + +## @param podManagementPolicy Statefulset Pod management policy, it needs to be Parallel to be able to complete the cluster join +## Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies +## +podManagementPolicy: Parallel + +## @param priorityClassName ClickHouse pods' priorityClassName +## +priorityClassName: "" +## @param topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template +## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods +## +topologySpreadConstraints: [] +## @param schedulerName Name of the k8s scheduler (other than default) for ClickHouse pods +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" +## @param terminationGracePeriodSeconds Seconds Redmine pod needs to terminate gracefully +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +## +terminationGracePeriodSeconds: "" +## @param lifecycleHooks for the ClickHouse container(s) to automate configuration before or after startup +## +lifecycleHooks: {} +## @param extraEnvVars Array with extra environment variables to add to ClickHouse nodes +## e.g: +## extraEnvVars: +## - name: FOO +## value: "bar" +## +extraEnvVars: [] +## @param extraEnvVarsCM Name of existing ConfigMap containing extra env vars for ClickHouse nodes +## +extraEnvVarsCM: "" +## @param extraEnvVarsSecret Name of existing Secret containing extra env vars for ClickHouse nodes +## +extraEnvVarsSecret: "" +## @param extraVolumes Optionally specify extra list of additional volumes for the ClickHouse pod(s) +## +extraVolumes: [] +## @param extraVolumeMounts Optionally specify extra list of additional volumeMounts for the ClickHouse container(s) +## +extraVolumeMounts: [] +## @param sidecars Add additional sidecar containers to the ClickHouse pod(s) +## e.g: +## sidecars: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +sidecars: [] +## @param initContainers Add additional init containers to the ClickHouse pod(s) +## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +## e.g: +## initContainers: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## command: ['sh', '-c', 'echo "hello world"'] +## +initContainers: [] + +## TLS configuration +## +tls: + ## @param tls.enabled Enable TLS traffic support + ## + enabled: false + ## @param tls.autoGenerated Generate automatically self-signed TLS certificates + ## + autoGenerated: false + ## @param tls.certificatesSecret Name of an existing secret that contains the certificates + ## + certificatesSecret: "" + ## @param tls.certFilename Certificate filename + ## + certFilename: "" + ## @param tls.certKeyFilename Certificate key filename + ## + certKeyFilename: "" + ## @param tls.certCAFilename CA Certificate filename + ## If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate + ## ref: https://www.postgresql.org/docs/9.6/auth-methods.html + ## + certCAFilename: "" + +## @section Traffic Exposure Parameters +## + +## ClickHouse service parameters +## +service: + ## @param service.type ClickHouse service type + ## + type: ClusterIP + ## @param service.ports.http ClickHouse service HTTP port + ## @param service.ports.https ClickHouse service HTTPS port + ## @param service.ports.tcp ClickHouse service TCP port + ## @param service.ports.tcpSecure ClickHouse service TCP (secure) port + ## @param service.ports.keeper ClickHouse keeper TCP container port + ## @param service.ports.keeperSecure ClickHouse keeper TCP (secure) container port + ## @param service.ports.keeperInter ClickHouse keeper interserver TCP container port + ## @param service.ports.mysql ClickHouse service MySQL port + ## @param service.ports.postgresql ClickHouse service PostgreSQL port + ## @param service.ports.interserver ClickHouse service Interserver port + ## @param service.ports.metrics ClickHouse service metrics port + ## + ports: + http: 8123 + https: 443 + tcp: 9000 + tcpSecure: 9440 + keeper: 2181 + keeperSecure: 3181 + keeperInter: 9444 + mysql: 9004 + postgresql: 9005 + interserver: 9009 + metrics: 8001 + ## Node ports to expose + ## @param service.nodePorts.http Node port for HTTP + ## @param service.nodePorts.https Node port for HTTPS + ## @param service.nodePorts.tcp Node port for TCP + ## @param service.nodePorts.tcpSecure Node port for TCP (with TLS) + ## @param service.nodePorts.keeper ClickHouse keeper TCP container port + ## @param service.nodePorts.keeperSecure ClickHouse keeper TCP (secure) container port + ## @param service.nodePorts.keeperInter ClickHouse keeper interserver TCP container port + ## @param service.nodePorts.mysql Node port for MySQL + ## @param service.nodePorts.postgresql Node port for PostgreSQL + ## @param service.nodePorts.interserver Node port for Interserver + ## @param service.nodePorts.metrics Node port for metrics + ## NOTE: choose port between <30000-32767> + ## + nodePorts: + http: "" + https: "" + tcp: "" + tcpSecure: "" + keeper: "" + keeperSecure: "" + keeperInter: "" + mysql: "" + postgresql: "" + interserver: "" + metrics: "" + ## @param service.clusterIP ClickHouse service Cluster IP + ## e.g.: + ## clusterIP: None + ## + clusterIP: "" + ## @param service.loadBalancerIP ClickHouse service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerIP: "" + ## @param service.loadBalancerSourceRanges ClickHouse service Load Balancer sources + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param service.externalTrafficPolicy ClickHouse service external traffic policy + ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param service.annotations Additional custom annotations for ClickHouse service + ## + annotations: {} + ## @param service.extraPorts Extra ports to expose in ClickHouse service (normally used with the `sidecars` value) + ## + extraPorts: [] + ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin + ## Values: ClientIP or None + ## ref: https://kubernetes.io/docs/user-guide/services/ + ## + sessionAffinity: None + ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## Headless service properties + ## + headless: + ## @param service.headless.annotations Annotations for the headless service. ## - ## If Configmap's enabled is `true`, Custom config.xml and metrica.xml - enabled: true + annotations: {} + +## External Access to ClickHouse configuration +## +externalAccess: + ## @param externalAccess.enabled Enable Kubernetes external cluster access to ClickHouse + ## + enabled: false + ## Parameters to configure K8s service(s) used to externally access ClickHouse + ## Note: A new service per will be created + ## + service: + ## @param externalAccess.service.type Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP ## - ## The maximum number of inbound connections. - max_connections: "4096" + type: LoadBalancer + ## @param externalAccess.service.ports.http ClickHouse service HTTP port + ## @param externalAccess.service.ports.https ClickHouse service HTTPS port + ## @param externalAccess.service.ports.tcp ClickHouse service TCP port + ## @param externalAccess.service.ports.tcpSecure ClickHouse service TCP (secure) port + ## @param externalAccess.service.ports.keeper ClickHouse keeper TCP container port + ## @param externalAccess.service.ports.keeperSecure ClickHouse keeper TCP (secure) container port + ## @param externalAccess.service.ports.keeperInter ClickHouse keeper interserver TCP container port + ## @param externalAccess.service.ports.mysql ClickHouse service MySQL port + ## @param externalAccess.service.ports.postgresql ClickHouse service PostgreSQL port + ## @param externalAccess.service.ports.interserver ClickHouse service Interserver port + ## @param externalAccess.service.ports.metrics ClickHouse service metrics port ## - ## The number of seconds that ClickHouse waits for incoming requests before closing the connection. - keep_alive_timeout: "3" + ports: + http: 80 + https: 443 + tcp: 9000 + tcpSecure: 9440 + keeper: 2181 + keeperSecure: 3181 + keeperInter: 9444 + mysql: 9004 + postgresql: 9005 + interserver: 9009 + metrics: 8001 + ## @param externalAccess.service.loadBalancerIPs Array of load balancer IPs for each ClickHouse . Length must be the same as replicaCount + ## e.g: + ## loadBalancerIPs: + ## - X.X.X.X + ## - Y.Y.Y.Y ## - ## The maximum number of simultaneously processed requests. - max_concurrent_queries: "100" + loadBalancerIPs: [] + ## @param externalAccess.service.loadBalancerAnnotations Array of load balancer annotations for each ClickHouse . Length must be the same as shards multiplied by replicaCount + ## e.g: + ## loadBalancerAnnotations: + ## - external-dns.alpha.kubernetes.io/hostname: 1.external.example.com. + ## - external-dns.alpha.kubernetes.io/hostname: 2.external.example.com. ## - ## Cache size (in bytes) for uncompressed data used by table engines from the MergeTree. - ## There is one shared cache for the server. Memory is allocated on demand. The cache is used if the option use_uncompressed_cache is enabled. - ## The uncompressed cache is advantageous for very short queries in individual cases. - uncompressed_cache_size: "8589934592" + loadBalancerAnnotations: [] + ## @param externalAccess.service.loadBalancerSourceRanges Address(es) that are allowed when service is LoadBalancer + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g: + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 ## - ## Approximate size (in bytes) of the cache of "marks" used by MergeTree. - ## The cache is shared for the server and memory is allocated as needed. The cache size must be at least 5368709120. - mark_cache_size: "5368709120" + loadBalancerSourceRanges: [] + ## @param externalAccess.service.nodePorts.http Node port for HTTP + ## @param externalAccess.service.nodePorts.https Node port for HTTPS + ## @param externalAccess.service.nodePorts.tcp Node port for TCP + ## @param externalAccess.service.nodePorts.tcpSecure Node port for TCP (with TLS) + ## @param externalAccess.service.nodePorts.keeper ClickHouse keeper TCP container port + ## @param externalAccess.service.nodePorts.keeperSecure ClickHouse keeper TCP container port (with TLS) + ## @param externalAccess.service.nodePorts.keeperInter ClickHouse keeper interserver TCP container port + ## @param externalAccess.service.nodePorts.mysql Node port for MySQL + ## @param externalAccess.service.nodePorts.postgresql Node port for PostgreSQL + ## @param externalAccess.service.nodePorts.interserver Node port for Interserver + ## @param externalAccess.service.nodePorts.metrics Node port for metrics + ## NOTE: choose port between <30000-32767> + ## e.g: + ## nodePorts: + ## tls: + ## - 30001 + ## - 30002 ## - ## You can specify umask here (see "man umask"). Server will apply it on startup. - ## Number is always parsed as octal. Default umask is 027 (other users cannot read logs, data files, etc; group can only read). - umask: "022" + nodePorts: + http: [] + https: [] + tcp: [] + tcpSecure: [] + keeper: [] + keeperSecure: [] + keeperInter: [] + mysql: [] + postgresql: [] + interserver: [] + metrics: [] + ## @param externalAccess.service.labels Service labels for external access ## - ## Perform mlockall after startup to lower first queries latency and to prevent clickhouse executable from being paged out under high IO load. - ## Enabling this option is recommended but will lead to increased startup time for up to a few seconds. - mlock_executable: false + labels: {} + ## @param externalAccess.service.annotations Service annotations for external access ## - ## The interval in seconds before reloading built-in dictionaries. - ## ClickHouse reloads built-in dictionaries every x seconds. This makes it possible to edit dictionaries "on the fly" without restarting the server. - builtin_dictionaries_reload_interval: "3600" + annotations: {} + ## @param externalAccess.service.extraPorts Extra ports to expose in the ClickHouse external service ## - ## Maximum session timeout, in seconds. - max_session_timeout: "3600" + extraPorts: [] + +## ClickHouse ingress parameters +## ref: http://kubernetes.io/docs/user-guide/ingress/ +## +ingress: + ## @param ingress.enabled Enable ingress record generation for ClickHouse + ## + enabled: false + ## @param ingress.pathType Ingress path type + ## + pathType: ImplementationSpecific + ## @param ingress.apiVersion Force Ingress API version (automatically detected if not set) + ## + apiVersion: "" + ## @param ingress.hostname Default host for the ingress record + ## + hostname: clickhouse.local + ## @param ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) + ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . + ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ + ## + ingressClassName: "" + ## @param ingress.path Default path for the ingress record + ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers + ## + path: / + ## @param ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. + ## Use this parameter to set the required annotations for cert-manager, see + ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations + ## e.g: + ## annotations: + ## kubernetes.io/ingress.class: nginx + ## cert-manager.io/cluster-issuer: cluster-issuer-name + ## + annotations: {} + ## @param ingress.tls Enable TLS configuration for the host defined at `ingress.hostname` parameter + ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.ingress.hostname }}` + ## You can: + ## - Use the `ingress.secrets` parameter to create this TLS secret + ## - Rely on cert-manager to create it by setting the corresponding annotations + ## - Rely on Helm to create self-signed certificates by setting `ingress.selfSigned=true` + ## + tls: false + ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm + ## + selfSigned: false + ## @param ingress.extraHosts An array with additional hostname(s) to be covered with the ingress record + ## e.g: + ## extraHosts: + ## - name: clickhouse.local + ## path: / + ## + extraHosts: [] + ## @param ingress.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host + ## e.g: + ## extraPaths: + ## - path: /* + ## backend: + ## serviceName: ssl-redirect + ## servicePort: use-annotation + ## + extraPaths: [] + ## @param ingress.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls + ## e.g: + ## extraTls: + ## - hosts: + ## - clickhouse.local + ## secretName: clickhouse.local-tls + ## + extraTls: [] + ## @param ingress.secrets Custom TLS certificates as secrets + ## NOTE: 'key' and 'certificate' are expected in PEM format + ## NOTE: 'name' should line up with a 'secretName' set further up + ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates + ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days + ## It is also possible to create and manage the certificates outside of this helm chart + ## Please see README.md for more information + ## e.g: + ## secrets: + ## - name: clickhouse.local-tls + ## key: |- + ## -----BEGIN RSA PRIVATE KEY----- + ## ... + ## -----END RSA PRIVATE KEY----- + ## certificate: |- + ## -----BEGIN CERTIFICATE----- + ## ... + ## -----END CERTIFICATE----- + ## + secrets: [] + ## @param ingress.extraRules Additional rules to be covered with this ingress record + ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules + ## e.g: + ## extraRules: + ## - host: example.local + ## http: + ## path: / + ## backend: + ## service: + ## name: example-svc + ## port: + ## name: http + ## + extraRules: [] + +## @section Persistence Parameters +## + +## Enable persistence using Persistent Volume Claims +## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ +## +persistence: + ## @param persistence.enabled Enable persistence using Persistent Volume Claims + ## + enabled: true + ## @param persistence.existingClaim Name of an existing PVC to use + ## + existingClaim: "" + ## @param persistence.storageClass Storage class of backing PVC + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + ## @param persistence.labels Persistent Volume Claim labels + ## + labels: {} + ## @param persistence.annotations Persistent Volume Claim annotations + ## + annotations: {} + ## @param persistence.accessModes Persistent Volume Access Modes + ## + accessModes: + - ReadWriteOnce + ## @param persistence.size Size of data volume + ## + size: 15Gi + ## @param persistence.selector Selector to match an existing Persistent Volume for ClickHouse data PVC + ## If set, the PVC can't have a PV dynamically provisioned for it + ## E.g. + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param persistence.dataSource Custom PVC data source + ## + dataSource: {} +## @section Init Container Parameters +## + +## 'volumePermissions' init container parameters +## Changes the owner and group of the persistent volume mount point to runAsUser:fsGroup values +## based on the *podSecurityContext/*containerSecurityContext parameters +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` + ## + enabled: false + ## OS Shell + Utility image + ## ref: https://hub.docker.com/r/bitnami/os-shell/tags/ + ## @param volumePermissions.image.registry [default: REGISTRY_NAME] OS Shell + Utility image registry + ## @param volumePermissions.image.repository [default: REPOSITORY_NAME/os-shell] OS Shell + Utility image repository + ## @skip volumePermissions.image.tag OS Shell + Utility image tag (immutable tags are recommended) + ## @param volumePermissions.image.pullPolicy OS Shell + Utility image pull policy + ## @param volumePermissions.image.pullSecrets OS Shell + Utility image pull secrets + ## + image: + registry: docker.io + repository: bitnami/os-shell + tag: 11-debian-11-r91 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName ## - ## Default session timeout, in seconds. - default_session_timeout: "60" + pullSecrets: [] + ## Init container's resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## @param volumePermissions.resources.limits The resources limits for the init container + ## @param volumePermissions.resources.requests The requested resources for the init container + ## + resources: + limits: {} + requests: {} + ## Init container Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param volumePermissions.containerSecurityContext.runAsUser Set init container's Security Context runAsUser + ## NOTE: when runAsUser is set to special value "auto", init container will try to chown the + ## data folder to auto-determined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` + ## "auto" is especially useful for OpenShift which has scc with dynamic user ids (and 0 is not allowed) + ## + containerSecurityContext: + runAsUser: 0 + +## @section Other Parameters +## + +## ServiceAccount configuration +## +serviceAccount: + ## @param serviceAccount.create Specifies whether a ServiceAccount should be created + ## + create: true + ## @param serviceAccount.name The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the common.names.fullname template + ## + name: "" + ## @param serviceAccount.annotations Additional Service Account annotations (evaluated as a template) + ## + annotations: {} + ## @param serviceAccount.automountServiceAccountToken Automount service account token for the server service account + ## + automountServiceAccountToken: true + +## Prometheus metrics +## +metrics: + ## @param metrics.enabled Enable the export of Prometheus metrics + ## + enabled: false + ## @param metrics.podAnnotations [object] Annotations for metrics scraping + ## + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .Values.containerPorts.metrics }}" + ## Prometheus Operator ServiceMonitor configuration + ## + serviceMonitor: + ## @param metrics.serviceMonitor.enabled if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) ## - ## Uncomment to disable ClickHouse internal DNS caching. - disable_internal_dns_cache: "1" + enabled: false + ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running ## - ## The maximum number of open files. - ## We recommend using this option in Mac OS X, since the getrlimit() function returns an incorrect value. - #max_open_files: + namespace: "" + ## @param metrics.serviceMonitor.annotations Additional custom annotations for the ServiceMonitor ## - ## The host name that can be used by other servers to access this server. - ## If omitted, it is defined in the same way as the hostname-f command. - ## Useful for breaking away from a specific network interface. - #interserver_http_host: + annotations: {} + ## @param metrics.serviceMonitor.labels Extra labels for the ServiceMonitor ## - ## Logging settings. - # path – The log path. Default value: /var/log/clickhouse-server. - # level – Logging level. Acceptable values: trace, debug, information, warning, error. Default value: /var/log/clickhouse-server - # size – Size of the file. Applies to loganderrorlog. Once the file reaches size, ClickHouse archives and renames it, and creates a new log file in its place. - # count – The number of archived log files that ClickHouse stores. - logger: - path: "/var/log/clickhouse-server" - level: "trace" - size: "1000M" - count: "10" + labels: {} + ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in Prometheus ## - ## Data compression settings. - # min_part_size – The minimum size of a table part. - # min_part_size_ratio – The ratio of the minimum size of a table part to the full size of the table. - # method – Compression method. Acceptable values ​: lz4 or zstd(experimental). - compression: - enabled: false - cases: - - min_part_size: "10000000000" - min_part_size_ratio: "0.01" - method: "zstd" + jobLabel: "" + ## @param metrics.serviceMonitor.honorLabels honorLabels chooses the metric's labels on collisions with target labels ## - ## Contains settings that allow ClickHouse to interact with a ZooKeeper cluster. - ## ClickHouse uses ZooKeeper for storing metadata of replicas when using replicated tables. If replicated tables are not used, this section of parameters can be omitted. - # node — ZooKeeper endpoint. You can set multiple endpoints. - # session_timeout — Maximum timeout for the client session in milliseconds. - # root — The znode that is used as the root for znodes used by the ClickHouse server. Optional. - # identity — User and password, that can be required by ZooKeeper to give access to requested znodes. Optional. - zookeeper_servers: - enabled: false - session_timeout_ms: "30000" - operation_timeout_ms: "10000" - #root: "/path/to/zookeeper/node" - #identity: "user:password" - config: - - index: "" - host: "" - port: "" + honorLabels: false + ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped. + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## e.g: + ## interval: 10s ## - ## Configuration of clusters used by the Distributed table engine. - ## The parameters host, port, and optionally user, password, secure, compression are specified for each server: - # host – The address of the remote server. - # port – The TCP port for messenger activity ('tcp_port' in the config, usually set to 9000). - # user – Name of the user for connecting to a remote server. Access is configured in the users.xml file. For more information, see the section "Access rights". - # password – The password for connecting to a remote server (not masked). - # secure - Use ssl for connection, usually you also should define port = 9440. Server should listen on 9440 and have correct certificates. - # compression - Use data compression. Default value: true. - remote_servers: - enabled: true - internal_replication: false - replica: - user: "default" - #password: "" - compression: true - backup: - enabled: true + interval: "" + ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## e.g: + ## scrapeTimeout: 10s ## - ## Sending data to Graphite. - # interval – The interval for sending, in seconds. - # timeout – The timeout for sending data, in seconds. - # root_path – Prefix for keys. - # metrics – Sending data from a :ref:system_tables-system.metrics table. - # events – Sending data from a :ref:system_tables-system.events table. - # asynchronous_metrics – Sending data from a :ref:system_tables-system.asynchronous_metrics table. - ## You can configure multiple clauses. For instance, you can use this for sending different data at different intervals. - graphite: - enabled: false - config: - - timeout: "0.1" - interval: "60" - root_path: "one_min" - metrics: true - events: true - events_cumulative: true - asynchronous_metrics: true + scrapeTimeout: "" + ## @param metrics.serviceMonitor.metricRelabelings Specify additional relabeling of metrics ## - ## A settings profile is a collection of settings grouped under the same name. - ## Each ClickHouse user has a profile. - ## To apply all the settings in a profile, set the profile setting. - ## More info: https://clickhouse.yandex/docs/en/operations/settings/settings_profiles/ - profiles: - enabled: false - profile: - - name: "default" - config: - max_memory_usage: "10000000000" - use_uncompressed_cache: "0" - load_balancing: "random" + metricRelabelings: [] + ## @param metrics.serviceMonitor.relabelings Specify general relabeling ## - ## The users section of the user.xml configuration file contains user settings. - ## More info: https://clickhouse.yandex/docs/en/operations/settings/settings_users/ - users: - enabled: false - user: - - name: "default" - config: - #password: "" - networks: - - "::/0" - profile: "default" - quota: "default" + relabelings: [] + ## @param metrics.serviceMonitor.selector Prometheus instance selector labels + ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration + ## selector: + ## prometheus: my-prometheus ## - ## Quotas allow you to limit resource usage over a period of time, or simply track the use of resources. - ## Quotas are set up in the user config. This is usually 'users.xml'. - ## More info: https://clickhouse.yandex/docs/en/operations/quotas/ - quotas: - enabled: false - quota: - - name: "default" - config: - - duration: "3600" - queries: "0" - errors: "0" - result_rows: "0" - read_rows: "0" - execution_time: "0" - -## -## Web interface for ClickHouse in the Tabix project. -## Features: -# Works with ClickHouse directly from the browser, without the need to install additional software. -# Query editor with syntax highlighting. -# Auto-completion of commands. -# Tools for graphical analysis of query execution. -# Color scheme options. -tabix: - ## - ## Enable Tabix - enabled: true - ## - ## ## The instance number of Tabix - replicas: "1" + selector: {} + + ## Prometheus Operator PrometheusRule configuration ## - ## The deployment strategy to use to replace existing pods with new ones. - updateStrategy: + prometheusRule: + ## @param metrics.prometheusRule.enabled Create a PrometheusRule for Prometheus Operator ## - ## Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate. - type: "RollingUpdate" - ## - ## The maximum number of pods that can be scheduled above the desired number of pods. - maxSurge: 3 - ## - ## The maximum number of pods that can be unavailable during the update. - maxUnavailable: 1 - ## - ## Docker image name. - image: "spoonest/clickhouse-tabix-web-client" - ## - ## Docker image version - imageVersion: "stable" - ## - ## Image pull policy. One of Always, Never, IfNotPresent. - ## Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. - ## More info: https://kubernetes.io/docs/concepts/containers/images#updating-images - imagePullPolicy: "IfNotPresent" - ## Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. - ## More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes - livenessProbe: - enabled: true - initialDelaySeconds: "30" - periodSeconds: "30" - timeoutSeconds: "5" - failureThreshold: "3" - successThreshold: "1" - ## Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. - ## More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes - readinessProbe: - enabled: true - initialDelaySeconds: "30" - periodSeconds: "30" - timeoutSeconds: "5" - failureThreshold: "3" - successThreshold: "1" - ## - ## ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. - ## If specified, these secrets will be passed to individual puller implementations for them to use. - ## For example, in the case of docker, only DockerConfig type secrets are honored. - ## More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod - #imagePullSecrets: - ## - ## You can limit access to your tabix.ui application on the proxy level. - ## User and Password parameters to restrict access only for specified user. - security: - user: "admin" - password: "admin" - ## - ## You can automatically connect to a Clickhouse server by specifying chName, chHost, chHost, chPassword and/or chParams environment variables. - #automaticConnection: - # chName: "test" - # chHost: "test" - # chLogin: "test" - # chPassword: "test" - # chParams: "" - ## - ## An API object that manages external access to the services in a cluster, typically HTTP. - ## Ingress can provide load balancing, SSL termination and name-based virtual hosting. - ingress: enabled: false - # host: "tabix.domain.com" - # path: "/" - # tls: - # enabled: false - # hosts: - # - "tabix.domain.com" - # - "tabix.domain1.com" - # secretName: "tabix-secret" + ## @param metrics.prometheusRule.namespace Namespace for the PrometheusRule Resource (defaults to the Release Namespace) + ## + namespace: "" + ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so PrometheusRule will be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.prometheusRule.rules PrometheusRule definitions + ## - alert: ClickhouseServerRestart + ## annotations: + ## message: Clickhouse-server started recently + ## expr: ClickHouseAsyncMetrics_Uptime > 1 < 180 + ## for: 5m + ## labels: + ## severity: warning + rules: [] + +## @section External Zookeeper paramaters +## +externalZookeeper: + ## @param externalZookeeper.servers List of external zookeeper servers to use + ## @param externalZookeeper.port Port of the Zookeeper servers + ## + servers: [] + port: 2888 + +## @section Zookeeper subchart parameters +## +## @param zookeeper.enabled Deploy Zookeeper subchart +## @param zookeeper.replicaCount Number of Zookeeper instances +## @param zookeeper.service.ports.client Zookeeper client port +## +zookeeper: + enabled: false + ## Override zookeeper default image as 3.9 is not supported https://github.com/ClickHouse/ClickHouse/issues/53749 + ## ref: https://github.com/bitnami/containers/tree/main/bitnami/zookeeper + ## @param zookeeper.image.registry [default: REGISTRY_NAME] Zookeeper image registry + ## @param zookeeper.image.repository [default: REPOSITORY_NAME/zookeeper] Zookeeper image repository + ## @skip zookeeper.image.tag Zookeeper image tag (immutable tags are recommended) + ## @param zookeeper.image.pullPolicy Zookeeper image pull policy + image: + registry: docker.io + repository: bitnami/zookeeper + tag: 3.8.3-debian-11-r2 + pullPolicy: IfNotPresent + replicaCount: 3 + service: + ports: + client: 2181 From 318eadb249c79bdb46e13a003f86ebf5ac4af26a Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 5 Dec 2023 14:24:13 +0530 Subject: [PATCH 143/263] error-handle-changes-added --- client/pkg/clickhouse/db_client.go | 90 ++++++++++-------------------- 1 file changed, 30 insertions(+), 60 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 011fc0ff..82fd4359 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -136,14 +136,12 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertAzureContainerPushEvent)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -187,14 +185,12 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertQuayContainerPushEvent)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -240,14 +236,12 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertJfrogContainerPushEvent)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -293,13 +287,11 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertRakees)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -325,13 +317,11 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { func (c *DBClient) InsertKetallEvent(metrics model.Resource) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertKetall)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -356,13 +346,11 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertOutdated)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -389,13 +377,11 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertDepricatedApi)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -429,13 +415,11 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertDeletedApi)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -470,13 +454,11 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertKubvizEvent)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -539,13 +521,11 @@ func (c *DBClient) InsertContainerEvent(event string) { func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(InsertKubeScore) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -571,13 +551,11 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { for _, vulnerability := range result.Vulnerabilities { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(InsertTrivyVul) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } if _, err := stmt.Exec( metrics.ID, @@ -608,13 +586,11 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { for _, misconfiguration := range result.Misconfigurations { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(InsertTrivyMisconfig) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() @@ -654,13 +630,11 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { for _, vulnerability := range result.Vulnerabilities { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(InsertTrivyImage) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } if _, err := stmt.Exec( @@ -700,13 +674,11 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { if result.CycloneDX != nil { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(InsertTrivySbom) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } if _, err := stmt.Exec( @@ -834,14 +806,12 @@ func (c *DBClient) RetrieveKubvizEvent() ([]model.DbEvent, error) { func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { tx, err := c.conn.Begin() if err != nil { - log.Printf("error beginning transaction: %v", err) - return + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } stmt, err := tx.Prepare(string(InsertDockerHubBuild)) if err != nil { - log.Printf("error preparing statement: %v", err) - return + log.Fatalf("error preparing statement: %v", err) } defer stmt.Close() From dd490239ae6c263c27dbd29152d6ba068d5761ee Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 11 Dec 2023 12:49:57 +0530 Subject: [PATCH 144/263] Updated clickhouse dependency in client --- charts/clickhouse/.helmignore | 21 - charts/clickhouse/Chart.yaml | 23 - charts/clickhouse/README.md | 529 -------- charts/clickhouse/templates/NOTES.txt | 58 - charts/clickhouse/templates/_helpers.tpl | 219 ---- .../clickhouse/templates/configmap-extra.yaml | 20 - .../templates/configmap-users-extra.yaml | 20 - charts/clickhouse/templates/configmap.yaml | 20 - charts/clickhouse/templates/extra-list.yaml | 9 - .../templates/ingress-tls-secrets.yaml | 44 - charts/clickhouse/templates/ingress.yaml | 59 - .../templates/init-scripts-secret.yaml | 19 - .../clickhouse/templates/prometheusrule.yaml | 24 - .../templates/scripts-configmap.yaml | 34 - .../clickhouse/templates/service-account.yaml | 19 - .../templates/service-external-access.yaml | 155 --- .../templates/service-headless.yaml | 69 - charts/clickhouse/templates/service.yaml | 152 --- .../clickhouse/templates/servicemonitor.yaml | 47 - .../templates/start-scripts-secret.yaml | 19 - charts/clickhouse/templates/statefulset.yaml | 425 ------- charts/clickhouse/templates/tls-secret.yaml | 29 - charts/clickhouse/values.yaml | 1131 ----------------- charts/client/Chart.yaml | 6 +- .../configmap-clickhouse-datasource.yaml | 6 +- .../configmap-vertamedia-datasource.yaml | 9 +- charts/client/templates/deployment.yaml | 14 +- charts/client/values.yaml | 8 +- 28 files changed, 30 insertions(+), 3158 deletions(-) delete mode 100644 charts/clickhouse/.helmignore delete mode 100644 charts/clickhouse/Chart.yaml delete mode 100644 charts/clickhouse/README.md delete mode 100644 charts/clickhouse/templates/NOTES.txt delete mode 100644 charts/clickhouse/templates/_helpers.tpl delete mode 100644 charts/clickhouse/templates/configmap-extra.yaml delete mode 100644 charts/clickhouse/templates/configmap-users-extra.yaml delete mode 100644 charts/clickhouse/templates/configmap.yaml delete mode 100644 charts/clickhouse/templates/extra-list.yaml delete mode 100644 charts/clickhouse/templates/ingress-tls-secrets.yaml delete mode 100644 charts/clickhouse/templates/ingress.yaml delete mode 100644 charts/clickhouse/templates/init-scripts-secret.yaml delete mode 100644 charts/clickhouse/templates/prometheusrule.yaml delete mode 100644 charts/clickhouse/templates/scripts-configmap.yaml delete mode 100644 charts/clickhouse/templates/service-account.yaml delete mode 100644 charts/clickhouse/templates/service-external-access.yaml delete mode 100644 charts/clickhouse/templates/service-headless.yaml delete mode 100644 charts/clickhouse/templates/service.yaml delete mode 100644 charts/clickhouse/templates/servicemonitor.yaml delete mode 100644 charts/clickhouse/templates/start-scripts-secret.yaml delete mode 100644 charts/clickhouse/templates/statefulset.yaml delete mode 100644 charts/clickhouse/templates/tls-secret.yaml delete mode 100644 charts/clickhouse/values.yaml diff --git a/charts/clickhouse/.helmignore b/charts/clickhouse/.helmignore deleted file mode 100644 index f0c13194..00000000 --- a/charts/clickhouse/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/charts/clickhouse/Chart.yaml b/charts/clickhouse/Chart.yaml deleted file mode 100644 index 83ead92e..00000000 --- a/charts/clickhouse/Chart.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v2 -appVersion: 23.10.5 -dependencies: -- name: common - repository: https://charts.bitnami.com/bitnami - tags: - - bitnami-common - version: 2.x.x -description: ClickHouse is an open-source column-oriented OLAP database management - system. Use it to boost your database performance while providing linear scalability - and hardware efficiency. -home: https://bitnami.com -icon: https://bitnami.com/assets/stacks/clickhouse/img/clickhouse-stack-220x234.png -keywords: -- database -- sharding -maintainers: -- name: VMware, Inc. - url: https://github.com/bitnami/charts -name: clickhouse -sources: -- https://github.com/bitnami/charts/tree/main/bitnami/clickhouse -version: 1.0.3 diff --git a/charts/clickhouse/README.md b/charts/clickhouse/README.md deleted file mode 100644 index ef654f7e..00000000 --- a/charts/clickhouse/README.md +++ /dev/null @@ -1,529 +0,0 @@ - - -# Bitnami package for ClickHouse - -ClickHouse is an open-source column-oriented OLAP database management system. Use it to boost your database performance while providing linear scalability and hardware efficiency. - -[Overview of ClickHouse](https://clickhouse.com/) - -Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. - -## TL;DR - -```console -helm install my-release oci://registry-1.docker.io/bitnamicharts/clickhouse -``` - -Looking to use ClickHouse in production? Try [VMware Tanzu Application Catalog](https://bitnami.com/enterprise), the enterprise edition of Bitnami Application Catalog. - -## Introduction - -Bitnami charts for Helm are carefully engineered, actively maintained and are the quickest and easiest way to deploy containers on a Kubernetes cluster that are ready to handle production workloads. - -This chart bootstraps a [ClickHouse](https://github.com/clickhouse/clickhouse) Deployment in a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. - -Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. - -[Learn more about the default configuration of the chart](https://docs.bitnami.com/kubernetes/infrastructure/clickhouse/get-started/). - -## Prerequisites - -- Kubernetes 1.23+ -- Helm 3.8.0+ -- PV provisioner support in the underlying infrastructure -- ReadWriteMany volumes for deployment scaling - -> If you are using Kubernetes 1.18, the following code needs to be commented out. -> seccompProfile: -> type: "RuntimeDefault" - -## Installing the Chart - -To install the chart with the release name `my-release`: - -```console -helm install my-release oci://REGISTRY_NAME/REPOSITORY_NAME/clickhouse -``` - -> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. - -The command deploys ClickHouse on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. - -> **Tip**: List all releases using `helm list` - -## Uninstalling the Chart - -To uninstall/delete the `my-release` deployment: - -```console -helm delete my-release -``` - -The command removes all the Kubernetes components associated with the chart and deletes the release. - -## Parameters - -### Global parameters - -| Name | Description | Value | -| ------------------------- | ----------------------------------------------- | ----- | -| `global.imageRegistry` | Global Docker image registry | `""` | -| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | -| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | - -### Common parameters - -| Name | Description | Value | -| ------------------------ | --------------------------------------------------------------------------------------- | --------------- | -| `kubeVersion` | Override Kubernetes version | `""` | -| `nameOverride` | String to partially override common.names.name | `""` | -| `fullnameOverride` | String to fully override common.names.fullname | `""` | -| `namespaceOverride` | String to fully override common.names.namespace | `""` | -| `commonLabels` | Labels to add to all deployed objects | `{}` | -| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | -| `clusterDomain` | Kubernetes cluster domain name | `cluster.local` | -| `extraDeploy` | Array of extra objects to deploy with the release | `[]` | -| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | -| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | -| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | - -### ClickHouse Parameters - -| Name | Description | Value | -| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------- | -| `image.registry` | ClickHouse image registry | `REGISTRY_NAME` | -| `image.repository` | ClickHouse image repository | `REPOSITORY_NAME/clickhouse` | -| `image.digest` | ClickHouse image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | -| `image.pullPolicy` | ClickHouse image pull policy | `IfNotPresent` | -| `image.pullSecrets` | ClickHouse image pull secrets | `[]` | -| `image.debug` | Enable ClickHouse image debug mode | `false` | -| `shards` | Number of ClickHouse shards to deploy | `2` | -| `replicaCount` | Number of ClickHouse replicas per shard to deploy | `3` | -| `distributeReplicasByZone` | Schedules replicas of the same shard to different availability zones | `false` | -| `containerPorts.http` | ClickHouse HTTP container port | `8123` | -| `containerPorts.https` | ClickHouse HTTPS container port | `8443` | -| `containerPorts.tcp` | ClickHouse TCP container port | `9000` | -| `containerPorts.tcpSecure` | ClickHouse TCP (secure) container port | `9440` | -| `containerPorts.keeper` | ClickHouse keeper TCP container port | `2181` | -| `containerPorts.keeperSecure` | ClickHouse keeper TCP (secure) container port | `3181` | -| `containerPorts.keeperInter` | ClickHouse keeper interserver TCP container port | `9444` | -| `containerPorts.mysql` | ClickHouse MySQL container port | `9004` | -| `containerPorts.postgresql` | ClickHouse PostgreSQL container port | `9005` | -| `containerPorts.interserver` | ClickHouse Interserver container port | `9009` | -| `containerPorts.metrics` | ClickHouse metrics container port | `8001` | -| `livenessProbe.enabled` | Enable livenessProbe on ClickHouse containers | `true` | -| `livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `10` | -| `livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | -| `livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | -| `livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | -| `livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | -| `readinessProbe.enabled` | Enable readinessProbe on ClickHouse containers | `true` | -| `readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `10` | -| `readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | -| `readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | -| `readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | -| `readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | -| `startupProbe.enabled` | Enable startupProbe on ClickHouse containers | `false` | -| `startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `10` | -| `startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | -| `startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | -| `startupProbe.failureThreshold` | Failure threshold for startupProbe | `3` | -| `startupProbe.successThreshold` | Success threshold for startupProbe | `1` | -| `customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | -| `customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | -| `customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | -| `resources.limits` | The resources limits for the ClickHouse containers | `{}` | -| `resources.requests` | The requested resources for the ClickHouse containers | `{}` | -| `podSecurityContext.enabled` | Enabled ClickHouse pods' Security Context | `true` | -| `podSecurityContext.fsGroup` | Set ClickHouse pod's Security Context fsGroup | `1001` | -| `containerSecurityContext.enabled` | Enable containers' Security Context | `true` | -| `containerSecurityContext.runAsUser` | Set containers' Security Context runAsUser | `1001` | -| `containerSecurityContext.runAsNonRoot` | Set containers' Security Context runAsNonRoot | `true` | -| `containerSecurityContext.readOnlyRootFilesystem` | Set read only root file system pod's | `false` | -| `containerSecurityContext.privileged` | Set contraller container's Security Context privileged | `false` | -| `containerSecurityContext.allowPrivilegeEscalation` | Set contraller container's Security Context allowPrivilegeEscalation | `false` | -| `containerSecurityContext.capabilities.drop` | List of capabilities to be droppedn | `["ALL"]` | -| `containerSecurityContext.seccompProfile.type` | Set container's Security Context seccomp profile | `RuntimeDefault` | -| `auth.username` | ClickHouse Admin username | `default` | -| `auth.password` | ClickHouse Admin password | `""` | -| `auth.existingSecret` | Name of a secret containing the Admin password | `""` | -| `auth.existingSecretKey` | Name of the key inside the existing secret | `""` | -| `logLevel` | Logging level | `information` | - -### ClickHouse keeper configuration parameters - -| Name | Description | Value | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------- | -| `keeper.enabled` | Deploy ClickHouse keeper. Support is experimental. | `false` | -| `defaultConfigurationOverrides` | Default configuration overrides (evaluated as a template) | `""` | -| `existingOverridesConfigmap` | The name of an existing ConfigMap with your custom configuration for ClickHouse | `""` | -| `extraOverrides` | Extra configuration overrides (evaluated as a template) apart from the default | `""` | -| `extraOverridesConfigmap` | The name of an existing ConfigMap with extra configuration for ClickHouse | `""` | -| `extraOverridesSecret` | The name of an existing ConfigMap with your custom configuration for ClickHouse | `""` | -| `usersExtraOverrides` | Users extra configuration overrides (evaluated as a template) apart from the default | `""` | -| `usersExtraOverridesConfigmap` | The name of an existing ConfigMap with users extra configuration for ClickHouse | `""` | -| `usersExtraOverridesSecret` | The name of an existing ConfigMap with your custom users configuration for ClickHouse | `""` | -| `initdbScripts` | Dictionary of initdb scripts | `{}` | -| `initdbScriptsSecret` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) | `""` | -| `startdbScripts` | Dictionary of startdb scripts | `{}` | -| `startdbScriptsSecret` | ConfigMap with the startdb scripts (Note: Overrides `startdbScripts`) | `""` | -| `command` | Override default container command (useful when using custom images) | `["/scripts/setup.sh"]` | -| `args` | Override default container args (useful when using custom images) | `[]` | -| `hostAliases` | ClickHouse pods host aliases | `[]` | -| `podLabels` | Extra labels for ClickHouse pods | `{}` | -| `podAnnotations` | Annotations for ClickHouse pods | `{}` | -| `podAffinityPreset` | Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `nodeAffinityPreset.type` | Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `nodeAffinityPreset.key` | Node label key to match. Ignored if `affinity` is set | `""` | -| `nodeAffinityPreset.values` | Node label values to match. Ignored if `affinity` is set | `[]` | -| `affinity` | Affinity for ClickHouse pods assignment | `{}` | -| `nodeSelector` | Node labels for ClickHouse pods assignment | `{}` | -| `tolerations` | Tolerations for ClickHouse pods assignment | `[]` | -| `updateStrategy.type` | ClickHouse statefulset strategy type | `RollingUpdate` | -| `podManagementPolicy` | Statefulset Pod management policy, it needs to be Parallel to be able to complete the cluster join | `Parallel` | -| `priorityClassName` | ClickHouse pods' priorityClassName | `""` | -| `topologySpreadConstraints` | Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template | `[]` | -| `schedulerName` | Name of the k8s scheduler (other than default) for ClickHouse pods | `""` | -| `terminationGracePeriodSeconds` | Seconds Redmine pod needs to terminate gracefully | `""` | -| `lifecycleHooks` | for the ClickHouse container(s) to automate configuration before or after startup | `{}` | -| `extraEnvVars` | Array with extra environment variables to add to ClickHouse nodes | `[]` | -| `extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for ClickHouse nodes | `""` | -| `extraEnvVarsSecret` | Name of existing Secret containing extra env vars for ClickHouse nodes | `""` | -| `extraVolumes` | Optionally specify extra list of additional volumes for the ClickHouse pod(s) | `[]` | -| `extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the ClickHouse container(s) | `[]` | -| `sidecars` | Add additional sidecar containers to the ClickHouse pod(s) | `[]` | -| `initContainers` | Add additional init containers to the ClickHouse pod(s) | `[]` | -| `tls.enabled` | Enable TLS traffic support | `false` | -| `tls.autoGenerated` | Generate automatically self-signed TLS certificates | `false` | -| `tls.certificatesSecret` | Name of an existing secret that contains the certificates | `""` | -| `tls.certFilename` | Certificate filename | `""` | -| `tls.certKeyFilename` | Certificate key filename | `""` | -| `tls.certCAFilename` | CA Certificate filename | `""` | - -### Traffic Exposure Parameters - -| Name | Description | Value | -| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | -| `service.type` | ClickHouse service type | `ClusterIP` | -| `service.ports.http` | ClickHouse service HTTP port | `8123` | -| `service.ports.https` | ClickHouse service HTTPS port | `443` | -| `service.ports.tcp` | ClickHouse service TCP port | `9000` | -| `service.ports.tcpSecure` | ClickHouse service TCP (secure) port | `9440` | -| `service.ports.keeper` | ClickHouse keeper TCP container port | `2181` | -| `service.ports.keeperSecure` | ClickHouse keeper TCP (secure) container port | `3181` | -| `service.ports.keeperInter` | ClickHouse keeper interserver TCP container port | `9444` | -| `service.ports.mysql` | ClickHouse service MySQL port | `9004` | -| `service.ports.postgresql` | ClickHouse service PostgreSQL port | `9005` | -| `service.ports.interserver` | ClickHouse service Interserver port | `9009` | -| `service.ports.metrics` | ClickHouse service metrics port | `8001` | -| `service.nodePorts.http` | Node port for HTTP | `""` | -| `service.nodePorts.https` | Node port for HTTPS | `""` | -| `service.nodePorts.tcp` | Node port for TCP | `""` | -| `service.nodePorts.tcpSecure` | Node port for TCP (with TLS) | `""` | -| `service.nodePorts.keeper` | ClickHouse keeper TCP container port | `""` | -| `service.nodePorts.keeperSecure` | ClickHouse keeper TCP (secure) container port | `""` | -| `service.nodePorts.keeperInter` | ClickHouse keeper interserver TCP container port | `""` | -| `service.nodePorts.mysql` | Node port for MySQL | `""` | -| `service.nodePorts.postgresql` | Node port for PostgreSQL | `""` | -| `service.nodePorts.interserver` | Node port for Interserver | `""` | -| `service.nodePorts.metrics` | Node port for metrics | `""` | -| `service.clusterIP` | ClickHouse service Cluster IP | `""` | -| `service.loadBalancerIP` | ClickHouse service Load Balancer IP | `""` | -| `service.loadBalancerSourceRanges` | ClickHouse service Load Balancer sources | `[]` | -| `service.externalTrafficPolicy` | ClickHouse service external traffic policy | `Cluster` | -| `service.annotations` | Additional custom annotations for ClickHouse service | `{}` | -| `service.extraPorts` | Extra ports to expose in ClickHouse service (normally used with the `sidecars` value) | `[]` | -| `service.sessionAffinity` | Control where client requests go, to the same pod or round-robin | `None` | -| `service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | -| `service.headless.annotations` | Annotations for the headless service. | `{}` | -| `externalAccess.enabled` | Enable Kubernetes external cluster access to ClickHouse | `false` | -| `externalAccess.service.type` | Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP | `LoadBalancer` | -| `externalAccess.service.ports.http` | ClickHouse service HTTP port | `80` | -| `externalAccess.service.ports.https` | ClickHouse service HTTPS port | `443` | -| `externalAccess.service.ports.tcp` | ClickHouse service TCP port | `9000` | -| `externalAccess.service.ports.tcpSecure` | ClickHouse service TCP (secure) port | `9440` | -| `externalAccess.service.ports.keeper` | ClickHouse keeper TCP container port | `2181` | -| `externalAccess.service.ports.keeperSecure` | ClickHouse keeper TCP (secure) container port | `3181` | -| `externalAccess.service.ports.keeperInter` | ClickHouse keeper interserver TCP container port | `9444` | -| `externalAccess.service.ports.mysql` | ClickHouse service MySQL port | `9004` | -| `externalAccess.service.ports.postgresql` | ClickHouse service PostgreSQL port | `9005` | -| `externalAccess.service.ports.interserver` | ClickHouse service Interserver port | `9009` | -| `externalAccess.service.ports.metrics` | ClickHouse service metrics port | `8001` | -| `externalAccess.service.loadBalancerIPs` | Array of load balancer IPs for each ClickHouse . Length must be the same as replicaCount | `[]` | -| `externalAccess.service.loadBalancerAnnotations` | Array of load balancer annotations for each ClickHouse . Length must be the same as shards multiplied by replicaCount | `[]` | -| `externalAccess.service.loadBalancerSourceRanges` | Address(es) that are allowed when service is LoadBalancer | `[]` | -| `externalAccess.service.nodePorts.http` | Node port for HTTP | `[]` | -| `externalAccess.service.nodePorts.https` | Node port for HTTPS | `[]` | -| `externalAccess.service.nodePorts.tcp` | Node port for TCP | `[]` | -| `externalAccess.service.nodePorts.tcpSecure` | Node port for TCP (with TLS) | `[]` | -| `externalAccess.service.nodePorts.keeper` | ClickHouse keeper TCP container port | `[]` | -| `externalAccess.service.nodePorts.keeperSecure` | ClickHouse keeper TCP container port (with TLS) | `[]` | -| `externalAccess.service.nodePorts.keeperInter` | ClickHouse keeper interserver TCP container port | `[]` | -| `externalAccess.service.nodePorts.mysql` | Node port for MySQL | `[]` | -| `externalAccess.service.nodePorts.postgresql` | Node port for PostgreSQL | `[]` | -| `externalAccess.service.nodePorts.interserver` | Node port for Interserver | `[]` | -| `externalAccess.service.nodePorts.metrics` | Node port for metrics | `[]` | -| `externalAccess.service.labels` | Service labels for external access | `{}` | -| `externalAccess.service.annotations` | Service annotations for external access | `{}` | -| `externalAccess.service.extraPorts` | Extra ports to expose in the ClickHouse external service | `[]` | -| `ingress.enabled` | Enable ingress record generation for ClickHouse | `false` | -| `ingress.pathType` | Ingress path type | `ImplementationSpecific` | -| `ingress.apiVersion` | Force Ingress API version (automatically detected if not set) | `""` | -| `ingress.hostname` | Default host for the ingress record | `clickhouse.local` | -| `ingress.ingressClassName` | IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) | `""` | -| `ingress.path` | Default path for the ingress record | `/` | -| `ingress.annotations` | Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. | `{}` | -| `ingress.tls` | Enable TLS configuration for the host defined at `ingress.hostname` parameter | `false` | -| `ingress.selfSigned` | Create a TLS secret for this ingress record using self-signed certificates generated by Helm | `false` | -| `ingress.extraHosts` | An array with additional hostname(s) to be covered with the ingress record | `[]` | -| `ingress.extraPaths` | An array with additional arbitrary paths that may need to be added to the ingress under the main host | `[]` | -| `ingress.extraTls` | TLS configuration for additional hostname(s) to be covered with this ingress record | `[]` | -| `ingress.secrets` | Custom TLS certificates as secrets | `[]` | -| `ingress.extraRules` | Additional rules to be covered with this ingress record | `[]` | - -### Persistence Parameters - -| Name | Description | Value | -| --------------------------- | ----------------------------------------------------------------------- | ------------------- | -| `persistence.enabled` | Enable persistence using Persistent Volume Claims | `true` | -| `persistence.existingClaim` | Name of an existing PVC to use | `""` | -| `persistence.storageClass` | Storage class of backing PVC | `""` | -| `persistence.labels` | Persistent Volume Claim labels | `{}` | -| `persistence.annotations` | Persistent Volume Claim annotations | `{}` | -| `persistence.accessModes` | Persistent Volume Access Modes | `["ReadWriteOnce"]` | -| `persistence.size` | Size of data volume | `8Gi` | -| `persistence.selector` | Selector to match an existing Persistent Volume for ClickHouse data PVC | `{}` | -| `persistence.dataSource` | Custom PVC data source | `{}` | - -### Init Container Parameters - -| Name | Description | Value | -| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | -------------------------- | -| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` | -| `volumePermissions.image.registry` | OS Shell + Utility image registry | `REGISTRY_NAME` | -| `volumePermissions.image.repository` | OS Shell + Utility image repository | `REPOSITORY_NAME/os-shell` | -| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` | -| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` | -| `volumePermissions.resources.limits` | The resources limits for the init container | `{}` | -| `volumePermissions.resources.requests` | The requested resources for the init container | `{}` | -| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` | - -### Other Parameters - -| Name | Description | Value | -| --------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------- | -| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | -| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` | -| `serviceAccount.annotations` | Additional Service Account annotations (evaluated as a template) | `{}` | -| `serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | -| `metrics.enabled` | Enable the export of Prometheus metrics | `false` | -| `metrics.podAnnotations` | Annotations for metrics scraping | `{}` | -| `metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) | `false` | -| `metrics.serviceMonitor.namespace` | Namespace in which Prometheus is running | `""` | -| `metrics.serviceMonitor.annotations` | Additional custom annotations for the ServiceMonitor | `{}` | -| `metrics.serviceMonitor.labels` | Extra labels for the ServiceMonitor | `{}` | -| `metrics.serviceMonitor.jobLabel` | The name of the label on the target service to use as the job name in Prometheus | `""` | -| `metrics.serviceMonitor.honorLabels` | honorLabels chooses the metric's labels on collisions with target labels | `false` | -| `metrics.serviceMonitor.interval` | Interval at which metrics should be scraped. | `""` | -| `metrics.serviceMonitor.scrapeTimeout` | Timeout after which the scrape is ended | `""` | -| `metrics.serviceMonitor.metricRelabelings` | Specify additional relabeling of metrics | `[]` | -| `metrics.serviceMonitor.relabelings` | Specify general relabeling | `[]` | -| `metrics.serviceMonitor.selector` | Prometheus instance selector labels | `{}` | -| `metrics.prometheusRule.enabled` | Create a PrometheusRule for Prometheus Operator | `false` | -| `metrics.prometheusRule.namespace` | Namespace for the PrometheusRule Resource (defaults to the Release Namespace) | `""` | -| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so PrometheusRule will be discovered by Prometheus | `{}` | -| `metrics.prometheusRule.rules` | PrometheusRule definitions | `[]` | - -### External Zookeeper paramaters - -| Name | Description | Value | -| --------------------------- | ----------------------------------------- | ------ | -| `externalZookeeper.servers` | List of external zookeeper servers to use | `[]` | -| `externalZookeeper.port` | Port of the Zookeeper servers | `2888` | - -### Zookeeper subchart parameters - -| Name | Description | Value | -| -------------------------------- | ----------------------------- | --------------------------- | -| `zookeeper.enabled` | Deploy Zookeeper subchart | `true` | -| `zookeeper.replicaCount` | Number of Zookeeper instances | `3` | -| `zookeeper.service.ports.client` | Zookeeper client port | `2181` | -| `zookeeper.image.registry` | Zookeeper image registry | `REGISTRY_NAME` | -| `zookeeper.image.repository` | Zookeeper image repository | `REPOSITORY_NAME/zookeeper` | -| `zookeeper.image.pullPolicy` | Zookeeper image pull policy | `IfNotPresent` | - -See to create the table. - -The above parameters map to the env variables defined in [bitnami/clickhouse](https://github.com/bitnami/containers/tree/main/bitnami/clickhouse). For more information please refer to the [bitnami/clickhouse](https://github.com/bitnami/containers/tree/main/bitnami/clickhouse) image documentation. - -Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, - -```console -helm install my-release \ - --set auth.username=admin \ - --set auth.password=password \ - oci://REGISTRY_NAME/REPOSITORY_NAME/clickhouse -``` - -> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. - -The above command sets the ClickHouse administrator account username and password to `admin` and `password` respectively. - -> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. - -Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, - -```console -helm install my-release -f values.yaml oci://REGISTRY_NAME/REPOSITORY_NAME/clickhouse -``` - -> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. -> **Tip**: You can use the default [values.yaml](https://github.com/bitnami/charts/tree/main/bitnami/clickhouse/values.yaml) - -## Configuration and installation details - -### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) - -It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. - -Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. - -### ClickHouse keeper support - -You can set `keeper.enabled` to use ClickHouse keeper. If `keeper.enabled=true`, Zookeeper settings will not be ignore. - -### External Zookeeper support - -You may want to have ClickHouse connect to an external zookeeper rather than installing one inside your cluster. Typical reasons for this are to use a managed database service, or to share a common database server for all your applications. To achieve this, the chart allows you to specify credentials for an external database with the [`externalZookeeper` parameter](#parameters). You should also disable the Zookeeper installation with the `zookeeper.enabled` option. Here is an example: - -```console -zookeper.enabled=false -externalZookeeper.host=myexternalhost -externalZookeeper.user=myuser -externalZookeeper.password=mypassword -externalZookeeper.database=mydatabase -externalZookeeper.port=3306 -``` - -### TLS secrets - -The chart also facilitates the creation of TLS secrets for use with the Ingress controller, with different options for certificate management. [Learn more about TLS secrets](https://docs.bitnami.com/kubernetes/infrastructure/clickhouse/administration/enable-tls-ingress/)). - -## Persistence - -The [Bitnami ClickHouse](https://github.com/bitnami/containers/tree/main/bitnami/clickhouse) image stores the ClickHouse data and configurations at the `/bitnami` path of the container. Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. - -### Additional environment variables - -In case you want to add extra environment variables (useful for advanced operations like custom init scripts), you can use the `extraEnvVars` property. - -```yaml -clickhouse: - extraEnvVars: - - name: LOG_LEVEL - value: error -``` - -Alternatively, you can use a ConfigMap or a Secret with the environment variables. To do so, use the `extraEnvVarsCM` or the `extraEnvVarsSecret` values. - -### Sidecars - -If additional containers are needed in the same pod as ClickHouse (such as additional metrics or logging exporters), they can be defined using the `sidecars` parameter. If these sidecars export extra ports, extra port definitions can be added using the `service.extraPorts` parameter. [Learn more about configuring and using sidecar containers](https://docs.bitnami.com/kubernetes/infrastructure/clickhouse/configuration/configure-sidecar-init-containers/). - -### Ingress without TLS - -For using ingress (example without TLS): - -```yaml -ingress: - ## If true, ClickHouse server Ingress will be created - ## - enabled: true - - ## ClickHouse server Ingress annotations - ## - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: 'true' - - ## ClickHouse server Ingress hostnames - ## Must be provided if Ingress is enabled - ## - hosts: - - clickhouse.domain.com -``` - -### Ingress TLS - -If your cluster allows automatic creation/retrieval of TLS certificates (e.g. [kube-lego](https://github.com/jetstack/kube-lego)), please refer to the documentation for that mechanism. - -To manually configure TLS, first create/retrieve a key & certificate pair for the address(es) you wish to protect. Then create a TLS secret (named `clickhouse-server-tls` in this example) in the namespace. Include the secret's name, along with the desired hostnames, in the Ingress TLS section of your custom `values.yaml` file: - -```yaml -ingress: - ## If true, ClickHouse server Ingress will be created - ## - enabled: true - - ## ClickHouse server Ingress annotations - ## - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: 'true' - - ## ClickHouse server Ingress hostnames - ## Must be provided if Ingress is enabled - ## - hosts: - - clickhouse.domain.com - - ## ClickHouse server Ingress TLS configuration - ## Secrets must be manually created in the namespace - ## - tls: - - secretName: clickhouse-server-tls - hosts: - - clickhouse.domain.com -``` - -### Using custom scripts - -For advanced operations, the Bitnami ClickHouse chart allows using custom init and start scripts that will be mounted in `/docker-entrypoint.initdb.d` and `/docker-entrypoint.startdb.d` . The `init` scripts will be run on the first boot whereas the `start` scripts will be run on every container start. For adding the scripts directly as values use the `initdbScripts` and `startdbScripts` values. For using Secrets use the `initdbScriptsSecret` and `startdbScriptsSecret`. - -```yaml -initdbScriptsSecret: init-scripts-secret -startdbScriptsSecret: start-scripts-secret -``` - -### Pod affinity - -This chart allows you to set your custom affinity using the `affinity` parameter. Find more information about Pod affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). - -As an alternative, use one of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `podAffinityPreset`, `podAntiAffinityPreset`, or `nodeAffinityPreset` parameters. - -## Troubleshooting - -Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). - -## Upgrading - -### To 2.0.0 - -This major updates the Zookeeper subchart to it newest major, 11.0.0. For more information on this subchart's major, please refer to [zookeeper upgrade notes](https://github.com/bitnami/charts/tree/main/bitnami/zookeeper#to-1100). - -## License - -Copyright © 2023 VMware, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file diff --git a/charts/clickhouse/templates/NOTES.txt b/charts/clickhouse/templates/NOTES.txt deleted file mode 100644 index 4bb61dab..00000000 --- a/charts/clickhouse/templates/NOTES.txt +++ /dev/null @@ -1,58 +0,0 @@ -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -** Please be patient while the chart is being deployed ** - -{{- if .Values.diagnosticMode.enabled }} -The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: - - command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} - args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} - -Get the list of pods by executing: - - kubectl get pods --namespace {{ include "common.names.namespace" . | quote }} -l app.kubernetes.io/instance={{ .Release.Name }} - -Access the pod you want to debug by executing - - kubectl exec --namespace {{ include "common.names.namespace" . | quote }} -ti -- bash - -In order to replicate the container startup scripts execute this command: - - /opt/bitnami/scripts/clickhouse/entrypoint.sh /opt/bitnami/scripts/clickhouse/run.sh - -{{- else }} - -ClickHouse is available in the following address: - -{{- if .Values.externalAccess.enabled }} - -NOTE: It may take a few minutes for the LoadBalancer IP to be available. - - kubectl get svc --namespace {{ template "common.names.namespace" . }} -l "app.kubernetes.io/name={{ template "common.names.fullname" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=clickhouse" -w - -{{- else if (eq "LoadBalancer" .Values.service.type) }} - - export SERVICE_IP=$(kubectl get svc --namespace {{ template "common.names.namespace" . }} {{ template "common.names.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") - -{{- else if (eq "NodePort" .Values.service.type)}} - - export NODE_IP=$(kubectl get nodes --namespace {{ template "common.names.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") - export NODE_PORT=$(kubectl get --namespace {{ template "common.names.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "common.names.fullname" . }}) - -{{- else if (eq "ClusterIP" .Values.service.type)}} - - kubectl port-forward --namespace {{ template "common.names.namespace" . }} svc/{{ template "common.names.fullname" . }} {{ .Values.service.ports.tcp }}:9000 & - -{{- end }} - -Credentials: - - echo "Username : {{ .Values.auth.username }}" - echo "Password : $(kubectl get secret --namespace {{ .Release.Namespace }} {{ include "clickhouse.secretName" . }} -o jsonpath="{.data.{{ include "clickhouse.secretKey" .}}}" | base64 -d)" - -{{- end }} - -{{- include "common.warnings.rollingTag" .Values.image }} -{{- include "clickhouse.validateValues" . }} diff --git a/charts/clickhouse/templates/_helpers.tpl b/charts/clickhouse/templates/_helpers.tpl deleted file mode 100644 index b5243526..00000000 --- a/charts/clickhouse/templates/_helpers.tpl +++ /dev/null @@ -1,219 +0,0 @@ -{{/* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{/* -Return the proper ClickHouse image name -*/}} -{{- define "clickhouse.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper image name (for the init container volume-permissions image) -*/}} -{{- define "clickhouse.volumePermissions.image" -}} -{{- include "common.images.image" ( dict "imageRoot" .Values.volumePermissions.image "global" .Values.global ) -}} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names -*/}} -{{- define "clickhouse.imagePullSecrets" -}} -{{- include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.volumePermissions.image) "global" .Values.global) -}} -{{- end -}} - -{{/* -Return true if a TLS credentials secret object should be created -*/}} -{{- define "clickhouse.createTlsSecret" -}} -{{- if and .Values.tls.autoGenerated (not .Values.tls.certificatesSecret) }} - {{- true -}} -{{- end -}} -{{- end -}} - -{{/* -Return the path to the CA cert file. -*/}} -{{- define "clickhouse.tlsSecretName" -}} -{{- if .Values.tls.autoGenerated }} - {{- printf "%s-crt" (include "common.names.fullname" .) -}} -{{- else -}} - {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} -{{- end -}} -{{- end -}} - -{{/* -Return the path to the cert file. -*/}} -{{- define "clickhouse.tlsCert" -}} -{{- if .Values.tls.autoGenerated }} - {{- printf "/opt/bitnami/clickhouse/certs/tls.crt" -}} -{{- else -}} - {{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/clickhouse/certs/%s" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the path to the cert key file. -*/}} -{{- define "clickhouse.tlsCertKey" -}} -{{- if .Values.tls.autoGenerated }} - {{- printf "/opt/bitnami/clickhouse/certs/tls.key" -}} -{{- else -}} -{{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/clickhouse/certs/%s" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the path to the CA cert file. -*/}} -{{- define "clickhouse.tlsCACert" -}} -{{- if .Values.tls.autoGenerated }} - {{- printf "/opt/bitnami/clickhouse/certs/ca.crt" -}} -{{- else -}} - {{- printf "/opt/bitnami/clickhouse/certs/%s" .Values.tls.certCAFilename -}} -{{- end -}} -{{- end -}} - -{{/* -Get the ClickHouse configuration configmap. -*/}} -{{- define "clickhouse.configmapName" -}} -{{- if .Values.existingOverridesConfigmap -}} - {{- .Values.existingOverridesConfigmap -}} -{{- else }} - {{- printf "%s" (include "common.names.fullname" . ) -}} -{{- end -}} -{{- end -}} - -{{/* -Get the ClickHouse configuration configmap. -*/}} -{{- define "clickhouse.extraConfigmapName" -}} -{{- if .Values.extraOverridesConfigmap -}} - {{- .Values.extraOverridesConfigmap -}} -{{- else }} - {{- printf "%s-extra" (include "common.names.fullname" . ) -}} -{{- end -}} -{{- end -}} - - -{{/* -Get the ClickHouse configuration users configmap. -*/}} -{{- define "clickhouse.usersExtraConfigmapName" -}} -{{- if .Values.usersExtraOverridesConfigmap -}} - {{- .Values.usersExtraOverridesConfigmap -}} -{{- else }} - {{- printf "%s-users-extra" (include "common.names.fullname" . ) -}} -{{- end -}} -{{- end -}} - -{{/* -Get the Clickhouse password secret name -*/}} -{{- define "clickhouse.secretName" -}} -{{- if .Values.auth.existingSecret -}} - {{- .Values.auth.existingSecret -}} -{{- else }} - {{- printf "%s" (include "common.names.fullname" . ) -}} -{{- end -}} -{{- end -}} - -{{/* -Get the ClickHouse password key inside the secret -*/}} -{{- define "clickhouse.secretKey" -}} -{{- if .Values.auth.existingSecret -}} - {{- .Values.auth.existingSecretKey -}} -{{- else }} - {{- print "admin-password" -}} -{{- end -}} -{{- end -}} - -{{/* -Get the startialization scripts Secret name. -*/}} -{{- define "clickhouse.startdbScriptsSecret" -}} -{{- if .Values.startdbScriptsSecret -}} - {{- printf "%s" (tpl .Values.startdbScriptsSecret $) -}} -{{- else -}} - {{- printf "%s-start-scripts" (include "common.names.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Get the initialization scripts Secret name. -*/}} -{{- define "clickhouse.initdbScriptsSecret" -}} -{{- if .Values.initdbScriptsSecret -}} - {{- printf "%s" (tpl .Values.initdbScriptsSecret $) -}} -{{- else -}} - {{- printf "%s-init-scripts" (include "common.names.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Return the path to the CA cert file. -*/}} -{{- define "clickhouse.headlessServiceName" -}} -{{- printf "%s-headless" (include "common.names.fullname" .) -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "clickhouse.zookeeper.fullname" -}} -{{- include "common.names.dependency.fullname" (dict "chartName" "zookeeper" "chartValues" .Values.zookeeper "context" $) -}} -{{- end -}} - -{{/* -Return the path to the CA cert file. -*/}} -{{- define "clickhouse.zookeeper.headlessServiceName" -}} -{{- printf "%s-headless" (include "clickhouse.zookeeper.fullname" .) -}} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "clickhouse.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} - -{{/* -Compile all warnings into a single message. -*/}} -{{- define "clickhouse.validateValues" -}} -{{- $messages := list -}} -{{- $messages := append $messages (include "clickhouse.validateValues.zookeeper" .) -}} -{{- $messages := without $messages "" -}} -{{- $message := join "\n" $messages -}} - -{{- if $message -}} -{{- printf "\nVALUES VALIDATION:\n%s" $message -}} -{{- end -}} -{{- end -}} - -{{/* Validate values of ClickHouse - [Zoo]keeper */}} -{{- define "clickhouse.validateValues.zookeeper" -}} -{{- if or (and .Values.keeper.enabled .Values.zookeeper.enabled) (and .Values.keeper.enabled .Values.externalZookeeper.servers) (and .Values.zookeeper.enabled .Values.externalZookeeper.servers) -}} -clickhouse: Multiple [Zoo]keeper - You can only use one [zoo]keeper - Please choose use ClickHouse keeper or - installing a Zookeeper chart (--set zookeeper.enabled=true) or - using an external instance (--set zookeeper.servers ) -{{- end -}} -{{- if and (not .Values.keeper.enabled) (not .Values.zookeeper.enabled) (not .Values.externalZookeeper.servers) (ne (int .Values.shards) 1) (ne (int .Values.replicaCount) 1) -}} -clickhouse: No [Zoo]keeper - If you are deploying more than one ClickHouse instance, you need to enable [Zoo]keeper. Please choose installing a [Zoo]keeper (--set keeper.enabled=true) or (--set zookeeper.enabled=true) or - using an external instance (--set zookeeper.servers ) -{{- end -}} -{{- end -}} diff --git a/charts/clickhouse/templates/configmap-extra.yaml b/charts/clickhouse/templates/configmap-extra.yaml deleted file mode 100644 index 153cf4d5..00000000 --- a/charts/clickhouse/templates/configmap-extra.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if and .Values.extraOverrides (not .Values.extraOverridesConfigmap) }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ printf "%s-extra" (include "common.names.fullname" .) }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - 01_extra_overrides.xml: | - {{- include "common.tplvalues.render" (dict "value" .Values.extraOverrides "context" $) | nindent 4 }} -{{- end }} diff --git a/charts/clickhouse/templates/configmap-users-extra.yaml b/charts/clickhouse/templates/configmap-users-extra.yaml deleted file mode 100644 index 056d2d02..00000000 --- a/charts/clickhouse/templates/configmap-users-extra.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if and .Values.usersExtraOverrides (not .Values.usersExtraOverridesConfigmap) }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ printf "%s-users-extra" (include "common.names.fullname" .) }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - 01_users_extra_overrides.xml: | - {{- include "common.tplvalues.render" (dict "value" .Values.usersExtraOverrides "context" $) | nindent 4 }} -{{- end }} diff --git a/charts/clickhouse/templates/configmap.yaml b/charts/clickhouse/templates/configmap.yaml deleted file mode 100644 index 2462712b..00000000 --- a/charts/clickhouse/templates/configmap.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if not .Values.existingOverridesConfigmap }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - 00_default_overrides.xml: | - {{- include "common.tplvalues.render" (dict "value" .Values.defaultConfigurationOverrides "context" $) | nindent 4 }} -{{- end }} diff --git a/charts/clickhouse/templates/extra-list.yaml b/charts/clickhouse/templates/extra-list.yaml deleted file mode 100644 index 2d35a580..00000000 --- a/charts/clickhouse/templates/extra-list.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- range .Values.extraDeploy }} ---- -{{ include "common.tplvalues.render" (dict "value" . "context" $) }} -{{- end }} diff --git a/charts/clickhouse/templates/ingress-tls-secrets.yaml b/charts/clickhouse/templates/ingress-tls-secrets.yaml deleted file mode 100644 index 6ef20e36..00000000 --- a/charts/clickhouse/templates/ingress-tls-secrets.yaml +++ /dev/null @@ -1,44 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if .Values.ingress.enabled }} -{{- if .Values.ingress.secrets }} -{{- range .Values.ingress.secrets }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .name }} - namespace: {{ $.Release.Namespace | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} - {{- if $.Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ .certificate | b64enc }} - tls.key: {{ .key | b64enc }} ---- -{{- end }} -{{- end }} -{{- if and .Values.ingress.tls .Values.ingress.selfSigned }} -{{- $secretName := printf "%s-tls" .Values.ingress.hostname }} -{{- $ca := genCA "clickhouse-ca" 365 }} -{{- $cert := genSignedCert .Values.ingress.hostname nil (list .Values.ingress.hostname) 365 $ca }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ $secretName }} - namespace: {{ .Release.Namespace | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} - tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} - ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} -{{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/ingress.yaml b/charts/clickhouse/templates/ingress.yaml deleted file mode 100644 index 7000eceb..00000000 --- a/charts/clickhouse/templates/ingress.yaml +++ /dev/null @@ -1,59 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if .Values.ingress.enabled }} -apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }} -kind: Ingress -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ .Release.Namespace | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- if or .Values.ingress.annotations .Values.commonAnnotations }} - {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.ingress.annotations .Values.commonAnnotations ) "context" . ) }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.ingressClassName (eq "true" (include "common.ingress.supportsIngressClassname" .)) }} - ingressClassName: {{ .Values.ingress.ingressClassName | quote }} - {{- end }} - rules: - {{- if .Values.ingress.hostname }} - - host: {{ .Values.ingress.hostname | quote }} - http: - paths: - {{- if .Values.ingress.extraPaths }} - {{- toYaml .Values.ingress.extraPaths | nindent 10 }} - {{- end }} - - path: {{ .Values.ingress.path }} - {{- if eq "true" (include "common.ingress.supportsPathType" .) }} - pathType: {{ .Values.ingress.pathType }} - {{- end }} - backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" .) "servicePort" "http" "context" $) | nindent 14 }} - {{- end }} - {{- range .Values.ingress.extraHosts }} - - host: {{ .name | quote }} - http: - paths: - - path: {{ default "/" .path }} - {{- if eq "true" (include "common.ingress.supportsPathType" $) }} - pathType: {{ default "ImplementationSpecific" .pathType }} - {{- end }} - backend: {{- include "common.ingress.backend" (dict "serviceName" (include "common.names.fullname" $) "servicePort" "http" "context" $) | nindent 14 }} - {{- end }} - {{- if .Values.ingress.extraRules }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraRules "context" $) | nindent 4 }} - {{- end }} - {{- if or (and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned)) .Values.ingress.extraTls }} - tls: - {{- if and .Values.ingress.tls (or (include "common.ingress.certManagerRequest" ( dict "annotations" .Values.ingress.annotations )) .Values.ingress.selfSigned) }} - - hosts: - - {{ .Values.ingress.hostname | quote }} - secretName: {{ printf "%s-tls" .Values.ingress.hostname }} - {{- end }} - {{- if .Values.ingress.extraTls }} - {{- include "common.tplvalues.render" (dict "value" .Values.ingress.extraTls "context" $) | nindent 4 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/init-scripts-secret.yaml b/charts/clickhouse/templates/init-scripts-secret.yaml deleted file mode 100644 index 32367093..00000000 --- a/charts/clickhouse/templates/init-scripts-secret.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if and .Values.initdbScripts (not .Values.initdbScriptsSecret) }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ printf "%s-init-scripts" (include "common.names.fullname" .) }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -stringData: -{{- include "common.tplvalues.render" (dict "value" .Values.initdbScripts "context" .) | nindent 2 }} -{{- end }} diff --git a/charts/clickhouse/templates/prometheusrule.yaml b/charts/clickhouse/templates/prometheusrule.yaml deleted file mode 100644 index dc2d05d3..00000000 --- a/charts/clickhouse/templates/prometheusrule.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled .Values.metrics.prometheusRule.rules }} -apiVersion: monitoring.coreos.com/v1 -kind: PrometheusRule -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ default .Release.Namespace .Values.metrics.prometheusRule.namespace | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: metrics - {{- if .Values.metrics.prometheusRule.additionalLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.additionalLabels "context" $ ) | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - groups: - - name: {{ include "common.names.fullname" . }} - rules: {{- toYaml .Values.metrics.prometheusRule.rules | nindent 8 }} -{{- end }} diff --git a/charts/clickhouse/templates/scripts-configmap.yaml b/charts/clickhouse/templates/scripts-configmap.yaml deleted file mode 100644 index 86aa34dc..00000000 --- a/charts/clickhouse/templates/scripts-configmap.yaml +++ /dev/null @@ -1,34 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -data: - setup.sh: |- - #!/bin/bash - - # Execute entrypoint as usual after obtaining KEEPER_SERVER_ID - # check KEEPER_SERVER_ID in persistent volume via myid - # if not present, set based on POD hostname - if [[ -f "/bitnami/clickhouse/keeper/data/myid" ]]; then - export KEEPER_SERVER_ID="$(cat /bitnami/clickhouse/keeper/data/myid)" - else - HOSTNAME="$(hostname -s)" - if [[ $HOSTNAME =~ (.*)-([0-9]+)$ ]]; then - export KEEPER_SERVER_ID=${BASH_REMATCH[2]} - else - echo "Failed to get index from hostname $HOST" - exit 1 - fi - fi - exec /opt/bitnami/scripts/clickhouse/entrypoint.sh /opt/bitnami/scripts/clickhouse/run.sh -- --listen_host=0.0.0.0 diff --git a/charts/clickhouse/templates/service-account.yaml b/charts/clickhouse/templates/service-account.yaml deleted file mode 100644 index 649086da..00000000 --- a/charts/clickhouse/templates/service-account.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if .Values.serviceAccount.create }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "clickhouse.serviceAccountName" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if or .Values.serviceAccount.annotations .Values.commonAnnotations }} - {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.serviceAccount.annotations .Values.commonAnnotations ) "context" . ) }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} - {{- end }} -automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} -{{- end }} diff --git a/charts/clickhouse/templates/service-external-access.yaml b/charts/clickhouse/templates/service-external-access.yaml deleted file mode 100644 index f50baa21..00000000 --- a/charts/clickhouse/templates/service-external-access.yaml +++ /dev/null @@ -1,155 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if $.Values.externalAccess.enabled }} -{{- $shards := $.Values.shards | int }} -{{- $replicas := $.Values.replicaCount | int }} -{{- $totalNodes := mul $shards $replicas }} -{{- range $shard, $e := until $shards }} -{{- range $i, $_e := until $replicas }} -{{- $loadBalancerAnnotationPosOffset := mul $shard $replicas }} -{{- $loadBalancerAnnotationPosition := add $loadBalancerAnnotationPosOffset $i }} -{{- $targetPod := printf "%s-shard%d-%d" (include "common.names.fullname" $) $shard $i }} -apiVersion: v1 -kind: Service -metadata: - name: {{ printf "%s-external" $targetPod | trunc 63 | trimSuffix "-" }} - namespace: {{ $.Release.Namespace | quote }} - {{- $labels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.externalAccess.service.labels $.Values.commonLabels ) "context" $ ) }} - labels: {{- include "common.labels.standard" ( dict "customLabels" $labels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - pod: {{ $targetPod }} - {{- if or $.Values.externalAccess.service.annotations $.Values.commonAnnotations $.Values.externalAccess.service.loadBalancerAnnotations }} - annotations: - {{- if and (not (empty $.Values.externalAccess.service.loadBalancerAnnotations)) (eq (len $.Values.externalAccess.service.loadBalancerAnnotations) $totalNodes) }} - {{ include "common.tplvalues.render" ( dict "value" (index $.Values.externalAccess.service.loadBalancerAnnotations $loadBalancerAnnotationPosition) "context" $) | nindent 4 }} - {{- end }} - {{- if $.Values.externalAccess.service.annotations }} - {{- include "common.tplvalues.render" ( dict "value" $.Values.externalAccess.service.annotations "context" $) | nindent 4 }} - {{- end }} - {{- if $.Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- end }} -spec: - type: {{ $.Values.externalAccess.service.type }} - {{- if eq $.Values.externalAccess.service.type "LoadBalancer" }} - {{- if and (not (empty $.Values.externalAccess.service.loadBalancerIPs)) (eq (len $.Values.externalAccess.service.loadBalancerIPs) $totalNodes) }} - loadBalancerIP: {{ index $.Values.externalAccess.service.loadBalancerIPs $i }} - {{- end }} - {{- if $.Values.externalAccess.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- toYaml $.Values.externalAccess.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} - {{- end }} - ports: - - name: http - port: {{ $.Values.externalAccess.service.ports.http }} - targetPort: http - {{- if not (empty $.Values.externalAccess.service.nodePorts.http) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.http $i }} - {{- else }} - nodePort: null - {{- end }} - {{- if $.Values.tls.enabled }} - - name: https - port: {{ $.Values.externalAccess.service.ports.https }} - targetPort: https - {{- if not (empty $.Values.externalAccess.service.nodePorts.https) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.https $i }} - {{- else }} - nodePort: null - {{- end }} - {{- end }} - {{- if $.Values.metrics.enabled }} - - name: http-metrics - port: {{ $.Values.externalAccess.service.ports.metrics }} - targetPort: http-metrics - {{- if not (empty $.Values.externalAccess.service.nodePorts.metrics) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.metrics $i }} - {{- else }} - nodePort: null - {{- end }} - {{- end }} - - name: tcp - port: {{ $.Values.externalAccess.service.ports.tcp }} - targetPort: tcp - {{- if not (empty $.Values.externalAccess.service.nodePorts.tcp) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.tcp $i }} - {{- else }} - nodePort: null - {{- end }} - {{- if $.Values.tls.enabled }} - - name: tcp-secure - port: {{ $.Values.externalAccess.service.ports.tcpSecure }} - targetPort: tcp-secure - {{- if not (empty $.Values.externalAccess.service.nodePorts.tcpSecure) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.tcpSecure $i }} - {{- else }} - nodePort: null - {{- end }} - {{- end }} - {{- if $.Values.keeper.enabled }} - - name: tcp-keeper - port: {{ $.Values.externalAccess.service.ports.keeper }} - targetPort: tcp-keeper - {{- if not (empty $.Values.externalAccess.service.nodePorts.keeper) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.keeper $i }} - {{- else }} - nodePort: null - {{- end }} - - name: tcp-keeperinter - port: {{ $.Values.externalAccess.service.ports.keeperInter }} - targetPort: tcp-keeperinter - {{- if not (empty $.Values.externalAccess.service.nodePorts.keeperInter) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.keeperInter $i }} - {{- else }} - nodePort: null - {{- end }} - {{- if $.Values.tls.enabled }} - - name: tcp-keepertls - port: {{ $.Values.externalAccess.service.ports.keeperSecure }} - targetPort: tcp-keepertls - {{- if not (empty $.Values.externalAccess.service.nodePorts.keeperSecure) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.keeperSecure $i }} - {{- else }} - nodePort: null - {{- end }} - {{- end }} - {{- end }} - - name: tcp-mysql - port: {{ $.Values.externalAccess.service.ports.mysql }} - targetPort: tcp-mysql - {{- if not (empty $.Values.externalAccess.service.nodePorts.mysql) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.mysql $i }} - {{- else }} - nodePort: null - {{- end }} - - name: tcp-postgresql - port: {{ $.Values.externalAccess.service.ports.postgresql }} - targetPort: tcp-postgresql - {{- if not (empty $.Values.externalAccess.service.nodePorts.postgresql) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.postgresql $i }} - {{- else }} - nodePort: null - {{- end }} - - name: tcp-intersrv - port: {{ $.Values.externalAccess.service.ports.interserver }} - targetPort: tcp-intersrv - {{- if not (empty $.Values.externalAccess.service.nodePorts.interserver) }} - nodePort: {{ index $.Values.externalAccess.service.nodePorts.interserver $i }} - {{- else }} - nodePort: null - {{- end }} - {{- if $.Values.externalAccess.service.extraPorts }} - {{- include "common.tplvalues.render" (dict "value" $.Values.externalAccess.service.extraPorts "context" $) | nindent 4 }} - {{- end }} - {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.podLabels $.Values.commonLabels ) "context" $ ) }} - selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - statefulset.kubernetes.io/pod-name: {{ $targetPod }} ---- -{{- end }} -{{- end }} -{{- end }} diff --git a/charts/clickhouse/templates/service-headless.yaml b/charts/clickhouse/templates/service-headless.yaml deleted file mode 100644 index f989841b..00000000 --- a/charts/clickhouse/templates/service-headless.yaml +++ /dev/null @@ -1,69 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -apiVersion: v1 -kind: Service -metadata: - name: {{ include "clickhouse.headlessServiceName" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if or .Values.service.headless.annotations .Values.commonAnnotations }} - {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.service.headless.annotations .Values.commonAnnotations ) "context" . ) }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} - {{- end }} -spec: - type: ClusterIP - clusterIP: None - publishNotReadyAddresses: true - ports: - - name: http - targetPort: http - port: {{ .Values.service.ports.http }} - protocol: TCP - - name: tcp - targetPort: tcp - port: {{ .Values.service.ports.tcp }} - protocol: TCP - {{- if .Values.tls.enabled }} - - name: tcp-secure - targetPort: tcp-secure - port: {{ .Values.service.ports.tcpSecure }} - protocol: TCP - {{- end }} - {{- if .Values.keeper.enabled }} - - name: tcp-keeper - targetPort: tcp-keeper - port: {{ .Values.service.ports.keeper }} - protocol: TCP - - name: tcp-keeperinter - targetPort: tcp-keeperinter - port: {{ .Values.service.ports.keeperInter }} - protocol: TCP - {{- if .Values.tls.enabled }} - - name: tcp-keepertls - targetPort: tcp-keepertls - port: {{ .Values.service.ports.keeperSecure }} - protocol: TCP - {{- end }} - {{- end }} - - name: tcp-mysql - targetPort: tcp-mysql - port: {{ .Values.service.ports.mysql }} - protocol: TCP - - name: tcp-postgresql - targetPort: tcp-postgresql - port: {{ .Values.service.ports.postgresql }} - protocol: TCP - - name: http-intersrv - targetPort: http-intersrv - port: {{ .Values.service.ports.interserver }} - protocol: TCP - {{- if .Values.service.extraPorts }} - {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} - {{- end }} - {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.podLabels .Values.commonLabels ) "context" . ) }} - selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse diff --git a/charts/clickhouse/templates/service.yaml b/charts/clickhouse/templates/service.yaml deleted file mode 100644 index f54e2268..00000000 --- a/charts/clickhouse/templates/service.yaml +++ /dev/null @@ -1,152 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -apiVersion: v1 -kind: Service -metadata: - name: {{ template "common.names.fullname" . }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if or .Values.service.annotations .Values.commonAnnotations }} - {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.service.annotations .Values.commonAnnotations ) "context" . ) }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.service.type }} - {{- if and .Values.service.clusterIP (eq .Values.service.type "ClusterIP") }} - clusterIP: {{ .Values.service.clusterIP }} - {{- end }} - {{- if .Values.service.sessionAffinity }} - sessionAffinity: {{ .Values.service.sessionAffinity }} - {{- end }} - {{- if .Values.service.sessionAffinityConfig }} - sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.service.sessionAffinityConfig "context" $) | nindent 4 }} - {{- end }} - {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") }} - externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy | quote }} - {{- end }} - {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerSourceRanges)) }} - loadBalancerSourceRanges: {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} - {{- if and (eq .Values.service.type "LoadBalancer") (not (empty .Values.service.loadBalancerIP)) }} - loadBalancerIP: {{ .Values.service.loadBalancerIP }} - {{- end }} - ports: - - name: http - targetPort: http - port: {{ .Values.service.ports.http }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.http)) }} - nodePort: {{ .Values.service.nodePorts.http }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- if .Values.tls.enabled }} - - name: https - targetPort: https - port: {{ .Values.service.ports.https }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.https)) }} - nodePort: {{ .Values.service.nodePorts.https }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- end }} - - name: tcp - targetPort: tcp - port: {{ .Values.service.ports.tcp }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcp)) }} - nodePort: {{ .Values.service.nodePorts.tcp }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- if .Values.tls.enabled }} - - name: tcp-secure - targetPort: tcp-secure - port: {{ .Values.service.ports.tcpSecure }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcpSecure)) }} - nodePort: {{ .Values.service.nodePorts.tcpSecure }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- end }} - {{- if .Values.keeper.enabled }} - - name: tcp-keeper - targetPort: tcp-keeper - port: {{ .Values.service.ports.keeper }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcp)) }} - nodePort: {{ .Values.service.nodePorts.keeper }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - - name: tcp-keeperinter - targetPort: tcp-keeperinter - port: {{ .Values.service.ports.keeperInter }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcp)) }} - nodePort: {{ .Values.service.nodePorts.keeperInter }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- if .Values.tls.enabled }} - - name: tcp-keepertls - targetPort: tcp-keepertls - port: {{ .Values.service.ports.keeperSecure }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.tcpSecure)) }} - nodePort: {{ .Values.service.nodePorts.keeperSecure }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- end }} - {{- end }} - - name: tcp-mysql - targetPort: tcp-mysql - port: {{ .Values.service.ports.mysql }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.mysql)) }} - nodePort: {{ .Values.service.nodePorts.mysql }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - - name: tcp-postgresql - targetPort: tcp-postgresql - port: {{ .Values.service.ports.postgresql }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.postgresql)) }} - nodePort: {{ .Values.service.nodePorts.postgresql }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - - name: http-intersrv - targetPort: http-intersrv - port: {{ .Values.service.ports.interserver }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.interserver)) }} - nodePort: {{ .Values.service.nodePorts.interserver }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- if .Values.metrics.enabled }} - - name: http-metrics - targetPort: http-metrics - port: {{ .Values.service.ports.metrics }} - protocol: TCP - {{- if and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePorts.metrics)) }} - nodePort: {{ .Values.service.nodePorts.metrics }} - {{- else if eq .Values.service.type "ClusterIP" }} - nodePort: null - {{- end }} - {{- end }} - {{- if .Values.service.extraPorts }} - {{- include "common.tplvalues.render" (dict "value" .Values.service.extraPorts "context" $) | nindent 4 }} - {{- end }} - {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.podLabels .Values.commonLabels ) "context" . ) }} - selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse diff --git a/charts/clickhouse/templates/servicemonitor.yaml b/charts/clickhouse/templates/servicemonitor.yaml deleted file mode 100644 index 2148b375..00000000 --- a/charts/clickhouse/templates/servicemonitor.yaml +++ /dev/null @@ -1,47 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ include "common.names.fullname" . }} - namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.namespace | quote }} - {{- $labels := include "common.tplvalues.merge" ( dict "values" ( list .Values.metrics.serviceMonitor.labels .Values.commonLabels ) "context" . ) }} - labels: {{- include "common.labels.standard" ( dict "customLabels" $labels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if or .Values.metrics.serviceMonitor.annotations .Values.commonAnnotations }} - {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.metrics.serviceMonitor.annotations .Values.commonAnnotations ) "context" . ) }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} - {{- end }} -spec: - jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel | quote }} - selector: - matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} - {{- if .Values.metrics.serviceMonitor.selector }} - {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} - {{- end }} - endpoints: - - port: http-metrics - path: "/metrics" - {{- if .Values.metrics.serviceMonitor.interval }} - interval: {{ .Values.metrics.serviceMonitor.interval }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} - scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.honorLabels }} - honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.metricRelabelings }} - metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.metrics.serviceMonitor.relabelings }} - relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 8 }} - {{- end }} - namespaceSelector: - matchNames: - - {{ include "common.names.namespace" . | quote }} -{{- end }} diff --git a/charts/clickhouse/templates/start-scripts-secret.yaml b/charts/clickhouse/templates/start-scripts-secret.yaml deleted file mode 100644 index c579f2e4..00000000 --- a/charts/clickhouse/templates/start-scripts-secret.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if and .Values.startdbScripts (not .Values.startdbScriptsSecret) }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ printf "%s-start-scripts" (include "common.names.fullname" .) }} - namespace: {{ include "common.names.namespace" . | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -stringData: -{{- include "common.tplvalues.render" (dict "value" .Values.startdbScripts "context" .) | nindent 2 }} -{{- end }} diff --git a/charts/clickhouse/templates/statefulset.yaml b/charts/clickhouse/templates/statefulset.yaml deleted file mode 100644 index 13d526ca..00000000 --- a/charts/clickhouse/templates/statefulset.yaml +++ /dev/null @@ -1,425 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- $shards := .Values.shards | int }} -{{- range $i, $e := until $shards }} -apiVersion: {{ include "common.capabilities.statefulset.apiVersion" $ }} -kind: StatefulSet -metadata: - name: {{ printf "%s-shard%d" (include "common.names.fullname" $ ) $i }} - namespace: {{ include "common.names.namespace" $ | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} - app.kubernetes.io/component: clickhouse - {{- if $.Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: - replicas: {{ $.Values.replicaCount }} - podManagementPolicy: {{ $.Values.podManagementPolicy | quote }} - {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.podLabels $.Values.commonLabels ) "context" $ ) }} - selector: - matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 6 }} - app.kubernetes.io/component: clickhouse - serviceName: {{ printf "%s-headless" (include "common.names.fullname" $) }} - {{- if $.Values.updateStrategy }} - updateStrategy: {{- toYaml $.Values.updateStrategy | nindent 4 }} - {{- end }} - template: - metadata: - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") $ | sha256sum }} - checksum/config-extra: {{ include (print $.Template.BasePath "/configmap-extra.yaml") $ | sha256sum }} - checksum/config-users-extra: {{ include (print $.Template.BasePath "/configmap-users-extra.yaml") $ | sha256sum }} - {{- if $.Values.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" $.Values.podAnnotations "context" $) | nindent 8 }} - {{- end }} - {{- if and $.Values.metrics.enabled $.Values.metrics.podAnnotations }} - {{- include "common.tplvalues.render" (dict "value" $.Values.metrics.podAnnotations "context" $) | nindent 8 }} - {{- end }} - labels: {{- include "common.labels.standard" ( dict "customLabels" $podLabels "context" $ ) | nindent 8 }} - app.kubernetes.io/component: clickhouse - shard: {{ $i | quote }} - spec: - serviceAccountName: {{ template "clickhouse.serviceAccountName" $ }} - {{- include "clickhouse.imagePullSecrets" $ | nindent 6 }} - {{- if $.Values.hostAliases }} - hostAliases: {{- include "common.tplvalues.render" (dict "value" $.Values.hostAliases "context" $) | nindent 8 }} - {{- end }} - {{- if $.Values.affinity }} - affinity: {{- include "common.tplvalues.render" ( dict "value" $.Values.affinity "context" $) | nindent 8 }} - {{- else }} - affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" $.Values.podAffinityPreset "component" "clickhouse" "customLabels" $podLabels "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" $.Values.podAntiAffinityPreset "component" "clickhouse" "customLabels" $podLabels "extraPodAffinityTerms" (ternary (list (dict "extraMatchLabels" (dict "shard" $i) "topologyKey" "topology.kubernetes.io/zone")) (list) $.Values.distributeReplicasByZone) "context" $) | nindent 10 }} - nodeAffinity: {{- include "common.affinities.nodes" (dict "type" $.Values.nodeAffinityPreset.type "key" $.Values.nodeAffinityPreset.key "values" $.Values.nodeAffinityPreset.values) | nindent 10 }} - {{- end }} - {{- if $.Values.nodeSelector }} - nodeSelector: {{- include "common.tplvalues.render" ( dict "value" $.Values.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if $.Values.tolerations }} - tolerations: {{- include "common.tplvalues.render" (dict "value" $.Values.tolerations "context" $) | nindent 8 }} - {{- end }} - {{- if $.Values.priorityClassName }} - priorityClassName: {{ $.Values.priorityClassName | quote }} - {{- end }} - {{- if $.Values.schedulerName }} - schedulerName: {{ $.Values.schedulerName | quote }} - {{- end }} - {{- if $.Values.topologySpreadConstraints }} - topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" $.Values.topologySpreadConstraints "context" $) | nindent 8 }} - {{- end }} - {{- if $.Values.podSecurityContext.enabled }} - securityContext: {{- omit $.Values.podSecurityContext "enabled" | toYaml | nindent 8 }} - {{- end }} - {{- if $.Values.terminationGracePeriodSeconds }} - terminationGracePeriodSeconds: {{ $.Values.terminationGracePeriodSeconds }} - {{- end }} - initContainers: - {{- if and $.Values.tls.enabled (not $.Values.volumePermissions.enabled) }} - - name: copy-certs - image: {{ include "clickhouse.volumePermissions.image" $ }} - imagePullPolicy: {{ $.Values.volumePermissions.image.pullPolicy | quote }} - {{- if $.Values.resources }} - resources: {{- toYaml $.Values.resources | nindent 12 }} - {{- end }} - {{- if $.Values.containerSecurityContext.enabled }} - # We don't require a privileged container in this case - securityContext: {{- omit $.Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} - command: - - /bin/sh - - -ec - - | - cp -L /tmp/certs/* /opt/bitnami/clickhouse/certs/ - chmod 600 {{ include "clickhouse.tlsCertKey" $ }} - volumeMounts: - - name: raw-certificates - mountPath: /tmp/certs - - name: clickhouse-certificates - mountPath: /opt/bitnami/clickhouse/certs - {{- else if and $.Values.volumePermissions.enabled $.Values.persistence.enabled }} - - name: volume-permissions - image: {{ include "clickhouse.volumePermissions.image" $ }} - imagePullPolicy: {{ $.Values.volumePermissions.image.pullPolicy | quote }} - command: - - /bin/sh - - -ec - - | - mkdir -p /bitnami/clickhouse/data - chmod 700 /bitnami/clickhouse/data - {{- if $.Values.keeper.enabled }} - mkdir -p /bitnami/clickhouse/keeper - chmod 700 /bitnami/clickhouse/keeper - {{- end }} - chown {{ $.Values.containerSecurityContext.runAsUser }}:{{ $.Values.podSecurityContext.fsGroup }} /bitnami/clickhouse - find /bitnami/clickhouse -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | \ - xargs -r chown -R {{ $.Values.containerSecurityContext.runAsUser }}:{{ $.Values.podSecurityContext.fsGroup }} - {{- if $.Values.tls.enabled }} - cp /tmp/certs/* /opt/bitnami/clickhouse/certs/ - {{- if eq ( toString ( $.Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} - chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/clickhouse/certs/ - {{- else }} - chown -R {{ $.Values.containerSecurityContext.runAsUser }}:{{ $.Values.podSecurityContext.fsGroup }} /opt/bitnami/clickhouse/certs/ - {{- end }} - chmod 600 {{ include "clickhouse.tlsCertKey" $ }} - {{- end }} - securityContext: {{- include "common.tplvalues.render" (dict "value" $.Values.volumePermissions.containerSecurityContext "context" $) | nindent 12 }} - {{- if $.Values.volumePermissions.resources }} - resources: {{- toYaml $.Values.volumePermissions.resources | nindent 12 }} - {{- end }} - volumeMounts: - - name: data - mountPath: /bitnami/clickhouse - {{- if $.Values.tls.enabled }} - - name: raw-certificates - mountPath: /tmp/certs - - name: clickhouse-certificates - mountPath: /opt/bitnami/clickhouse/certs - {{- end }} - {{- end }} - {{- if $.Values.initContainers }} - {{- include "common.tplvalues.render" (dict "value" $.Values.initContainers "context" $) | nindent 8 }} - {{- end }} - containers: - - name: clickhouse - image: {{ template "clickhouse.image" $ }} - imagePullPolicy: {{ $.Values.image.pullPolicy }} - {{- if $.Values.containerSecurityContext.enabled }} - securityContext: {{- omit $.Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- if $.Values.diagnosticMode.enabled }} - command: {{- include "common.tplvalues.render" (dict "value" $.Values.diagnosticMode.command "context" $) | nindent 12 }} - {{- else if $.Values.command }} - command: {{- include "common.tplvalues.render" (dict "value" $.Values.command "context" $) | nindent 12 }} - {{- end }} - {{- if $.Values.diagnosticMode.enabled }} - args: {{- include "common.tplvalues.render" (dict "value" $.Values.diagnosticMode.args "context" $) | nindent 12 }} - {{- else if $.Values.args }} - args: {{- include "common.tplvalues.render" (dict "value" $.Values.args "context" $) | nindent 12 }} - {{- end }} - env: - - name: BITNAMI_DEBUG - value: {{ ternary "true" "false" (or $.Values.image.debug $.Values.diagnosticMode.enabled) | quote }} - - name: CLICKHOUSE_HTTP_PORT - value: {{ $.Values.containerPorts.http | quote }} - - name: CLICKHOUSE_TCP_PORT - value: {{ $.Values.containerPorts.tcp | quote }} - - name: CLICKHOUSE_MYSQL_PORT - value: {{ $.Values.containerPorts.mysql | quote }} - - name: CLICKHOUSE_POSTGRESQL_PORT - value: {{ $.Values.containerPorts.postgresql | quote }} - - name: CLICKHOUSE_INTERSERVER_HTTP_PORT - value: {{ $.Values.containerPorts.interserver | quote }} - {{- if $.Values.tls.enabled }} - - name: CLICKHOUSE_TCP_SECURE_PORT - value: {{ $.Values.containerPorts.tcpSecure | quote }} - - name: CLICKHOUSE_HTTPS_PORT - value: {{ $.Values.containerPorts.https | quote }} - {{- end }} - {{- if $.Values.keeper.enabled }} - - name: CLICKHOUSE_KEEPER_PORT - value: {{ $.Values.containerPorts.keeper | quote }} - - name: CLICKHOUSE_KEEPER_INTER_PORT - value: {{ $.Values.containerPorts.keeperInter | quote }} - {{- if $.Values.tls.enabled }} - - name: CLICKHOUSE_KEEPER_SECURE_PORT - value: {{ $.Values.containerPorts.keeperSecure | quote }} - {{- end }} - {{- end }} - {{- if $.Values.metrics.enabled }} - - name: CLICKHOUSE_METRICS_PORT - value: {{ $.Values.containerPorts.metrics | quote }} - {{- end }} - - name: CLICKHOUSE_SHARD_ID - value: {{ printf "shard%d" $i | quote }} - - name: CLICKHOUSE_REPLICA_ID - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: ALLOW_EMPTY_PASSWORD - value: "yes" - {{- if $.Values.tls.enabled }} - - name: CLICKHOUSE_TLS_CERT_FILE - value: {{ include "clickhouse.tlsCert" $ | quote}} - - name: CLICKHOUSE_TLS_KEY_FILE - value: {{ include "clickhouse.tlsCertKey" $ | quote }} - - name: CLICKHOUSE_TLS_CA_FILE - value: {{ include "clickhouse.tlsCACert" $ | quote }} - {{- end }} - {{- if $.Values.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" $.Values.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- if $.Values.keeper.enabled }} - {{- $replicas := $.Values.replicaCount | int }} - {{- range $j, $r := until $replicas }} - - name: {{ printf "KEEPER_NODE_%d" $j }} - value: {{ printf "%s-shard%d-%d.%s.%s.svc.%s" (include "common.names.fullname" $ ) $i $j (include "clickhouse.headlessServiceName" $) (include "common.names.namespace" $) $.Values.clusterDomain }} - {{- end }} - {{- else if $.Values.zookeeper.enabled }} - {{- $replicas := $.Values.zookeeper.replicaCount | int }} - {{- range $j, $r := until $replicas }} - - name: {{ printf "KEEPER_NODE_%d" $j }} - value: {{ printf "%s-%d.%s.%s.svc.%s" (include "clickhouse.zookeeper.fullname" $ ) $j (include "clickhouse.zookeeper.headlessServiceName" $) (include "common.names.namespace" $) $.Values.clusterDomain }} - {{- end }} - {{- end }} - envFrom: - {{- if $.Values.extraEnvVarsCM }} - - configMapRef: - name: {{ include "common.tplvalues.render" (dict "value" $.Values.extraEnvVarsCM "context" $) }} - {{- end }} - {{- if $.Values.extraEnvVarsSecret }} - - secretRef: - name: {{ include "common.tplvalues.render" (dict "value" $.Values.extraEnvVarsSecret "context" $) }} - {{- end }} - {{- if $.Values.resources }} - resources: {{- toYaml $.Values.resources | nindent 12 }} - {{- end }} - ports: - - name: http - containerPort: {{ $.Values.containerPorts.http }} - - name: tcp - containerPort: {{ $.Values.containerPorts.tcp }} - {{- if $.Values.tls.enabled }} - - name: https - containerPort: {{ $.Values.containerPorts.https }} - - name: tcp-secure - containerPort: {{ $.Values.containerPorts.tcpSecure }} - {{- end }} - {{- if $.Values.keeper.enabled }} - - name: tcp-keeper - containerPort: {{ $.Values.containerPorts.keeper }} - - name: tcp-keeperinter - containerPort: {{ $.Values.containerPorts.keeperInter }} - {{- if $.Values.tls.enabled }} - - name: tcp-keepertls - containerPort: {{ $.Values.containerPorts.keeperSecure }} - {{- end }} - {{- end }} - - name: tcp-postgresql - containerPort: {{ $.Values.containerPorts.postgresql }} - - name: tcp-mysql - containerPort: {{ $.Values.containerPorts.mysql }} - - name: http-intersrv - containerPort: {{ $.Values.containerPorts.interserver }} - {{- if $.Values.metrics.enabled }} - - name: http-metrics - containerPort: {{ $.Values.containerPorts.metrics }} - {{- end }} - {{- if not $.Values.diagnosticMode.enabled }} - {{- if $.Values.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" $.Values.customLivenessProbe "context" $) | nindent 12 }} - {{- else if $.Values.livenessProbe.enabled }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit $.Values.livenessProbe "enabled") "context" $) | nindent 12 }} - httpGet: - path: /ping - port: http - {{- end }} - {{- if $.Values.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" $.Values.customReadinessProbe "context" $) | nindent 12 }} - {{- else if $.Values.readinessProbe.enabled }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit $.Values.readinessProbe "enabled") "context" $) | nindent 12 }} - httpGet: - path: /ping - port: http - {{- end }} - {{- if $.Values.customStartupProbe }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" $.Values.customStartupProbe "context" $) | nindent 12 }} - {{- else if $.Values.startupProbe.enabled }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit $.Values.startupProbe "enabled") "context" $) | nindent 12 }} - httpGet: - path: /ping - port: http - {{- end }} - {{- end }} - {{- if $.Values.lifecycleHooks }} - lifecycle: {{- include "common.tplvalues.render" (dict "value" $.Values.lifecycleHooks "context" $) | nindent 12 }} - {{- end }} - volumeMounts: - - name: scripts - mountPath: /scripts/setup.sh - subPath: setup.sh - - name: data - mountPath: /bitnami/clickhouse - - name: config - mountPath: /bitnami/clickhouse/etc/conf.d/default - {{- if or $.Values.extraOverridesConfigmap $.Values.extraOverrides }} - - name: extra-config - mountPath: /bitnami/clickhouse/etc/conf.d/extra-configmap - {{- end }} - {{- if or $.Values.usersExtraOverridesConfigmap $.Values.usersExtraOverrides }} - - name: users-extra-config - mountPath: /bitnami/clickhouse/etc/users.d/users-extra-configmap - {{- end }} - {{- if $.Values.extraOverridesSecret }} - - name: extra-secret - mountPath: /bitnami/clickhouse/etc/conf.d/extra-secret - {{- end }} - {{- if $.Values.usersExtraOverridesSecret }} - - name: users-extra-secret - mountPath: /bitnami/clickhouse/etc/users.d/users-extra-secret - {{- end }} - {{- if $.Values.tls.enabled }} - - name: clickhouse-certificates - mountPath: /bitnami/clickhouse/certs - {{- end }} - {{- if or $.Values.initdbScriptsSecret $.Values.initdbScripts }} - - name: custom-init-scripts - mountPath: /docker-entrypoint-initdb.d - {{- end }} - {{- if or $.Values.startdbScriptsSecret $.Values.startdbScripts }} - - name: custom-start-scripts - mountPath: /docker-entrypoint-startdb.d - {{- end }} - {{- if $.Values.extraVolumeMounts }} - {{- include "common.tplvalues.render" (dict "value" $.Values.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- if $.Values.sidecars }} - {{- include "common.tplvalues.render" ( dict "value" $.Values.sidecars "context" $) | nindent 8 }} - {{- end }} - volumes: - - name: scripts - configMap: - name: {{ printf "%s-scripts" (include "common.names.fullname" $) }} - defaultMode: 0755 - - name: config - configMap: - name: {{ template "clickhouse.configmapName" $ }} - {{- if or $.Values.initdbScriptsSecret $.Values.initdbScripts }} - - name: custom-init-scripts - secret: - secretName: {{ include "clickhouse.initdbScriptsSecret" $ }} - {{- end }} - {{- if or $.Values.startdbScriptsSecret $.Values.startdbScripts }} - - name: custom-start-scripts - secret: - secretName: {{ include "clickhouse.startdbScriptsSecret" $ }} - {{- end }} - {{- if or $.Values.extraOverridesConfigmap $.Values.extraOverrides }} - - name: extra-config - configMap: - name: {{ template "clickhouse.extraConfigmapName" $ }} - {{- end }} - {{- if or $.Values.usersExtraOverridesConfigmap $.Values.usersExtraOverrides }} - - name: users-extra-config - configMap: - name: {{ template "clickhouse.usersExtraConfigmapName" $ }} - {{- end }} - {{- if $.Values.extraOverridesSecret }} - - name: extra-secret - secret: - secretName: {{ $.Values.extraOverridesSecret }} - {{- end }} - {{- if $.Values.usersExtraOverridesSecret }} - - name: users-extra-secret - secret: - secretName: {{ $.Values.usersExtraOverridesSecret }} - {{- end }} - {{- if not $.Values.persistence.enabled }} - - name: data - emptyDir: {} - {{- else if $.Values.persistence.existingClaim }} - - name: data - persistentVolumeClaim: - claimName: {{ tpl $.Values.persistence.existingClaim $ }} - {{- end }} - {{- if $.Values.tls.enabled }} - - name: raw-certificates - secret: - secretName: {{ include "clickhouse.tlsSecretName" $ }} - - name: clickhouse-certificates - emptyDir: {} - {{- end }} - {{- if $.Values.extraVolumes }} - {{- include "common.tplvalues.render" (dict "value" $.Values.extraVolumes "context" $) | nindent 8 }} - {{- end }} - {{- if and $.Values.persistence.enabled (not $.Values.persistence.existingClaim) }} - volumeClaimTemplates: - - metadata: - name: data - {{- if or $.Values.persistence.annotations $.Values.commonAnnotations }} - {{- $claimAnnotations := include "common.tplvalues.merge" ( dict "values" ( list $.Values.persistence.annotations $.Values.commonLabels ) "context" $ ) }} - annotations: {{- include "common.tplvalues.render" ( dict "value" $claimAnnotations "context" $ ) | nindent 10 }} - {{- end }} - {{- $claimLabels := include "common.tplvalues.merge" ( dict "values" ( list $.Values.persistence.labels $.Values.commonLabels ) "context" $ ) }} - labels: {{- include "common.labels.matchLabels" ( dict "customLabels" $claimLabels "context" $ ) | nindent 10 }} - app.kubernetes.io/component: clickhouse - spec: - accessModes: - {{- range $.Values.persistence.accessModes }} - - {{ . | quote }} - {{- end }} - resources: - requests: - storage: {{ $.Values.persistence.size | quote }} - {{- if $.Values.persistence.selector }} - selector: {{- include "common.tplvalues.render" (dict "value" $.Values.persistence.selector "context" $) | nindent 10 }} - {{- end }} - {{- if $.Values.persistence.dataSource }} - dataSource: {{- include "common.tplvalues.render" (dict "value" $.Values.persistence.dataSource "context" $) | nindent 10 }} - {{- end }} - {{- include "common.storage.class" (dict "persistence" $.Values.persistence "global" $.Values.global) | nindent 8 }} - {{- end }} ---- -{{- end }} diff --git a/charts/clickhouse/templates/tls-secret.yaml b/charts/clickhouse/templates/tls-secret.yaml deleted file mode 100644 index 04b188e1..00000000 --- a/charts/clickhouse/templates/tls-secret.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- /* -Copyright VMware, Inc. -SPDX-License-Identifier: APACHE-2.0 -*/}} - -{{- if (include "clickhouse.createTlsSecret" . ) }} -{{- $secretName := printf "%s-crt" (include "common.names.fullname" .) }} -{{- $ca := genCA "clickhouse-ca" 365 }} -{{- $fullname := include "common.names.fullname" . }} -{{- $releaseNamespace := .Release.Namespace }} -{{- $clusterDomain := .Values.clusterDomain }} -{{- $primaryHeadlessServiceName := printf "%s-headless" (include "common.names.fullname" .)}} -{{- $altNames := list (printf "*.%s.%s.svc.%s" $fullname $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $fullname $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $primaryHeadlessServiceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $primaryHeadlessServiceName $releaseNamespace $clusterDomain) $fullname }} -{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ $secretName }} - namespace: {{ .Release.Namespace | quote }} - labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -type: kubernetes.io/tls -data: - tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} - tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} - ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} -{{- end }} diff --git a/charts/clickhouse/values.yaml b/charts/clickhouse/values.yaml deleted file mode 100644 index 444f13fa..00000000 --- a/charts/clickhouse/values.yaml +++ /dev/null @@ -1,1131 +0,0 @@ -# Copyright VMware, Inc. -# SPDX-License-Identifier: APACHE-2.0 - -## @section Global parameters -## Global Docker image parameters -## Please, note that this will override the image parameters, including dependencies, configured to use the global value -## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass -## - -## @param global.imageRegistry Global Docker image registry -## @param global.imagePullSecrets Global Docker registry secret names as an array -## @param global.storageClass Global StorageClass for Persistent Volume(s) -## -global: - imageRegistry: "" - ## E.g. - ## imagePullSecrets: - ## - myRegistryKeySecretName - ## - imagePullSecrets: [] - storageClass: "" - -## @section Common parameters -## - -## @param kubeVersion Override Kubernetes version -## -kubeVersion: "" -## @param nameOverride String to partially override common.names.name -## -nameOverride: "" -## @param fullnameOverride String to fully override common.names.fullname -## -fullnameOverride: "" -## @param namespaceOverride String to fully override common.names.namespace -## -namespaceOverride: "" -## @param commonLabels Labels to add to all deployed objects -## -commonLabels: {} -## @param commonAnnotations Annotations to add to all deployed objects -## -commonAnnotations: {} -## @param clusterDomain Kubernetes cluster domain name -## -clusterDomain: cluster.local -## @param extraDeploy Array of extra objects to deploy with the release -## -extraDeploy: [] - -## Enable diagnostic mode in the deployment -## -diagnosticMode: - ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) - ## - enabled: false - ## @param diagnosticMode.command Command to override all containers in the deployment - ## - command: - - sleep - ## @param diagnosticMode.args Args to override all containers in the deployment - ## - args: - - infinity - -## @section ClickHouse Parameters -## - -## Bitnami ClickHouse image -## ref: https://hub.docker.com/r/bitnami/clickhouse/tags/ -## @param image.registry [default: REGISTRY_NAME] ClickHouse image registry -## @param image.repository [default: REPOSITORY_NAME/clickhouse] ClickHouse image repository -## @skip image.tag ClickHouse image tag (immutable tags are recommended) -## @param image.digest ClickHouse image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag -## @param image.pullPolicy ClickHouse image pull policy -## @param image.pullSecrets ClickHouse image pull secrets -## @param image.debug Enable ClickHouse image debug mode -## -image: - registry: docker.io - repository: bitnami/clickhouse - tag: 23.10.5-debian-11-r0 - digest: "" - ## Specify a imagePullPolicy - ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' - ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images - ## - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## e.g: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## Enable debug mode - ## - debug: false -## @param shards Number of ClickHouse shards to deploy -## -shards: 2 - -## @param replicaCount Number of ClickHouse replicas per shard to deploy -## if keeper enable, same as keeper count, keeper cluster by shards. -## -replicaCount: 3 - -## @param distributeReplicasByZone Schedules replicas of the same shard to different availability zones -## -distributeReplicasByZone: false -## @param containerPorts.http ClickHouse HTTP container port -## @param containerPorts.https ClickHouse HTTPS container port -## @param containerPorts.tcp ClickHouse TCP container port -## @param containerPorts.tcpSecure ClickHouse TCP (secure) container port -## @param containerPorts.keeper ClickHouse keeper TCP container port -## @param containerPorts.keeperSecure ClickHouse keeper TCP (secure) container port -## @param containerPorts.keeperInter ClickHouse keeper interserver TCP container port -## @param containerPorts.mysql ClickHouse MySQL container port -## @param containerPorts.postgresql ClickHouse PostgreSQL container port -## @param containerPorts.interserver ClickHouse Interserver container port -## @param containerPorts.metrics ClickHouse metrics container port -## -containerPorts: - http: 8123 - https: 8443 - tcp: 9000 - tcpSecure: 9440 - keeper: 2181 - keeperSecure: 3181 - keeperInter: 9444 - mysql: 9004 - postgresql: 9005 - interserver: 9009 - metrics: 8001 -## Configure extra options for ClickHouse containers' liveness and readiness probes -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes -## @param livenessProbe.enabled Enable livenessProbe on ClickHouse containers -## @param livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe -## @param livenessProbe.periodSeconds Period seconds for livenessProbe -## @param livenessProbe.timeoutSeconds Timeout seconds for livenessProbe -## @param livenessProbe.failureThreshold Failure threshold for livenessProbe -## @param livenessProbe.successThreshold Success threshold for livenessProbe -## -livenessProbe: - enabled: true - failureThreshold: 3 - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 -## @param readinessProbe.enabled Enable readinessProbe on ClickHouse containers -## @param readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe -## @param readinessProbe.periodSeconds Period seconds for readinessProbe -## @param readinessProbe.timeoutSeconds Timeout seconds for readinessProbe -## @param readinessProbe.failureThreshold Failure threshold for readinessProbe -## @param readinessProbe.successThreshold Success threshold for readinessProbe -## -readinessProbe: - enabled: true - failureThreshold: 3 - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 -## @param startupProbe.enabled Enable startupProbe on ClickHouse containers -## @param startupProbe.initialDelaySeconds Initial delay seconds for startupProbe -## @param startupProbe.periodSeconds Period seconds for startupProbe -## @param startupProbe.timeoutSeconds Timeout seconds for startupProbe -## @param startupProbe.failureThreshold Failure threshold for startupProbe -## @param startupProbe.successThreshold Success threshold for startupProbe -## -startupProbe: - enabled: false - failureThreshold: 3 - initialDelaySeconds: 10 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 -## @param customLivenessProbe Custom livenessProbe that overrides the default one -## -customLivenessProbe: {} -## @param customReadinessProbe Custom readinessProbe that overrides the default one -## -customReadinessProbe: {} -## @param customStartupProbe Custom startupProbe that overrides the default one -## -customStartupProbe: {} -## ClickHouse resource requests and limits -## ref: http://kubernetes.io/docs/user-guide/compute-resources/ -## @param resources.limits The resources limits for the ClickHouse containers -## @param resources.requests The requested resources for the ClickHouse containers -## -resources: - limits: {} - requests: {} -## Configure Pods Security Context -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod -## @param podSecurityContext.enabled Enabled ClickHouse pods' Security Context -## @param podSecurityContext.fsGroup Set ClickHouse pod's Security Context fsGroup -## If you are using Kubernetes 1.18, the following code needs to be commented out. -## -podSecurityContext: - enabled: true - fsGroup: 1001 -## Configure Container Security Context -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container -## @param containerSecurityContext.enabled Enable containers' Security Context -## @param containerSecurityContext.runAsUser Set containers' Security Context runAsUser -## @param containerSecurityContext.runAsNonRoot Set containers' Security Context runAsNonRoot -## @param containerSecurityContext.readOnlyRootFilesystem Set read only root file system pod's -## @param containerSecurityContext.privileged Set contraller container's Security Context privileged -## @param containerSecurityContext.allowPrivilegeEscalation Set contraller container's Security Context allowPrivilegeEscalation -## @param containerSecurityContext.capabilities.drop List of capabilities to be droppedn -## @param containerSecurityContext.seccompProfile.type Set container's Security Context seccomp profile -## -containerSecurityContext: - enabled: true - runAsUser: 1001 - runAsNonRoot: true - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: false - capabilities: - drop: ["ALL"] - seccompProfile: - type: "RuntimeDefault" - -## Authentication -## @param auth.username ClickHouse Admin username -## @param auth.password ClickHouse Admin password -## @param auth.existingSecret Name of a secret containing the Admin password -## @param auth.existingSecretKey Name of the key inside the existing secret -## -auth: - username: "" - password: "" - existingSecret: "" - existingSecretKey: "" - -## @param logLevel Logging level -## -logLevel: information - -## @section ClickHouse keeper configuration parameters -## @param keeper.enabled Deploy ClickHouse keeper. Support is experimental. -## -keeper: - enabled: false - -## @param defaultConfigurationOverrides [string] Default configuration overrides (evaluated as a template) -## -defaultConfigurationOverrides: | - - - - - - {{ include "common.names.fullname" . }} - - - - {{ .Values.logLevel }} - - {{- if or (ne (int .Values.shards) 1) (ne (int .Values.replicaCount) 1)}} - - - - {{- $shards := $.Values.shards | int }} - {{- range $shard, $e := until $shards }} - - {{- $replicas := $.Values.replicaCount | int }} - {{- range $i, $_e := until $replicas }} - - {{ printf "%s-shard%d-%d.%s.%s.svc.%s" (include "common.names.fullname" $ ) $shard $i (include "clickhouse.headlessServiceName" $) (include "common.names.namespace" $) $.Values.clusterDomain }} - {{ $.Values.service.ports.tcp }} - - - - {{- end }} - - {{- end }} - - - {{- end }} - {{- if .Values.keeper.enabled }} - - - {{/*ClickHouse keeper configuration using the helm chart */}} - {{ $.Values.containerPorts.keeper }} - {{- if .Values.tls.enabled }} - {{ $.Values.containerPorts.keeperSecure }} - {{- end }} - - /bitnami/clickhouse/keeper/coordination/log - /bitnami/clickhouse/keeper/coordination/snapshots - - - 10000 - 30000 - trace - - - - {{- $nodes := .Values.replicaCount | int }} - {{- range $node, $e := until $nodes }} - - {{ $node | int }} - - {{ $.Values.service.ports.keeperInter }} - - {{- end }} - - - {{- end }} - {{- if or .Values.keeper.enabled .Values.zookeeper.enabled .Values.externalZookeeper.servers }} - - - {{- if or .Values.keeper.enabled }} - {{- $nodes := .Values.replicaCount | int }} - {{- range $node, $e := until $nodes }} - - - {{ $.Values.service.ports.keeper }} - - {{- end }} - {{- else if .Values.zookeeper.enabled }} - {{/* Zookeeper configuration using the helm chart */}} - {{- $nodes := .Values.zookeeper.replicaCount | int }} - {{- range $node, $e := until $nodes }} - - - {{ $.Values.zookeeper.service.ports.client }} - - {{- end }} - {{- else if .Values.externalZookeeper.servers }} - {{/* Zookeeper configuration using an external instance */}} - {{- range $node :=.Values.externalZookeeper.servers }} - - {{ $node }} - {{ $.Values.externalZookeeper.port }} - - {{- end }} - {{- end }} - - {{- end }} - {{- if .Values.tls.enabled }} - - - - - - {{- $certFileName := default "tls.crt" .Values.tls.certFilename }} - {{- $keyFileName := default "tls.key" .Values.tls.certKeyFilename }} - /bitnami/clickhouse/certs/{{$certFileName}} - /bitnami/clickhouse/certs/{{$keyFileName}} - none - true - sslv2,sslv3 - true - {{- if or .Values.tls.autoGenerated .Values.tls.certCAFilename }} - {{- $caFileName := default "ca.crt" .Values.tls.certCAFilename }} - /bitnami/clickhouse/certs/{{$caFileName}} - {{- else }} - true - {{- end }} - - - true - true - sslv2,sslv3 - true - none - - AcceptCertificateHandler - - - - {{- end }} - {{- if .Values.metrics.enabled }} - - - /metrics - - true - true - true - - {{- end }} - - -## @param existingOverridesConfigmap The name of an existing ConfigMap with your custom configuration for ClickHouse -## -existingOverridesConfigmap: "" - -## @param extraOverrides Extra configuration overrides (evaluated as a template) apart from the default -## -extraOverrides: "" - -## @param extraOverridesConfigmap The name of an existing ConfigMap with extra configuration for ClickHouse -## -extraOverridesConfigmap: "" - -## @param extraOverridesSecret The name of an existing ConfigMap with your custom configuration for ClickHouse -## -extraOverridesSecret: "" - -## @param usersExtraOverrides Users extra configuration overrides (evaluated as a template) apart from the default -## -usersExtraOverrides: "" - -## @param usersExtraOverridesConfigmap The name of an existing ConfigMap with users extra configuration for ClickHouse -## -usersExtraOverridesConfigmap: "" - -## @param usersExtraOverridesSecret The name of an existing ConfigMap with your custom users configuration for ClickHouse -## -usersExtraOverridesSecret: "" - -## @param initdbScripts Dictionary of initdb scripts -## Specify dictionary of scripts to be run at first boot -## Example: -## initdbScripts: -## my_init_script.sh: | -## #!/bin/bash -## echo "Do something." -## -initdbScripts: {} -## @param initdbScriptsSecret ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) -## -initdbScriptsSecret: "" - -## @param startdbScripts Dictionary of startdb scripts -## Specify dictionary of scripts to be run on every start -## Example: -## startdbScripts: -## my_start_script.sh: | -## #!/bin/bash -## echo "Do something." -## -startdbScripts: {} -## @param startdbScriptsSecret ConfigMap with the startdb scripts (Note: Overrides `startdbScripts`) -## -startdbScriptsSecret: "" - -## @param command Override default container command (useful when using custom images) -## -command: - - /scripts/setup.sh -## @param args Override default container args (useful when using custom images) -## -args: [] -## @param hostAliases ClickHouse pods host aliases -## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ -## -hostAliases: [] -## @param podLabels Extra labels for ClickHouse pods -## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ -## -podLabels: {} -## @param podAnnotations Annotations for ClickHouse pods -## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ -## -podAnnotations: {} -## @param podAffinityPreset Pod affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` -## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity -## -podAffinityPreset: "" -## @param podAntiAffinityPreset Pod anti-affinity preset. Ignored if `affinity` is set. Allowed values: `soft` or `hard` -## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity -## -podAntiAffinityPreset: soft -## Node affinity preset -## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity -## -nodeAffinityPreset: - ## @param nodeAffinityPreset.type Node affinity preset type. Ignored if `affinity` is set. Allowed values: `soft` or `hard` - ## - type: "" - ## @param nodeAffinityPreset.key Node label key to match. Ignored if `affinity` is set - ## - key: "" - ## @param nodeAffinityPreset.values Node label values to match. Ignored if `affinity` is set - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] -## @param affinity Affinity for ClickHouse pods assignment -## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -## NOTE: `podAffinityPreset`, `podAntiAffinityPreset`, and `nodeAffinityPreset` will be ignored when it's set -## -affinity: {} -## @param nodeSelector Node labels for ClickHouse pods assignment -## ref: https://kubernetes.io/docs/user-guide/node-selection/ -## -nodeSelector: {} -## @param tolerations Tolerations for ClickHouse pods assignment -## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ -## -tolerations: [] -## @param updateStrategy.type ClickHouse statefulset strategy type -## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies -## -updateStrategy: - ## StrategyType - ## Can be set to RollingUpdate or OnDelete - ## - type: RollingUpdate - -## @param podManagementPolicy Statefulset Pod management policy, it needs to be Parallel to be able to complete the cluster join -## Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies -## -podManagementPolicy: Parallel - -## @param priorityClassName ClickHouse pods' priorityClassName -## -priorityClassName: "" -## @param topologySpreadConstraints Topology Spread Constraints for pod assignment spread across your cluster among failure-domains. Evaluated as a template -## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#spread-constraints-for-pods -## -topologySpreadConstraints: [] -## @param schedulerName Name of the k8s scheduler (other than default) for ClickHouse pods -## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ -## -schedulerName: "" -## @param terminationGracePeriodSeconds Seconds Redmine pod needs to terminate gracefully -## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods -## -terminationGracePeriodSeconds: "" -## @param lifecycleHooks for the ClickHouse container(s) to automate configuration before or after startup -## -lifecycleHooks: {} -## @param extraEnvVars Array with extra environment variables to add to ClickHouse nodes -## e.g: -## extraEnvVars: -## - name: FOO -## value: "bar" -## -extraEnvVars: [] -## @param extraEnvVarsCM Name of existing ConfigMap containing extra env vars for ClickHouse nodes -## -extraEnvVarsCM: "" -## @param extraEnvVarsSecret Name of existing Secret containing extra env vars for ClickHouse nodes -## -extraEnvVarsSecret: "" -## @param extraVolumes Optionally specify extra list of additional volumes for the ClickHouse pod(s) -## -extraVolumes: [] -## @param extraVolumeMounts Optionally specify extra list of additional volumeMounts for the ClickHouse container(s) -## -extraVolumeMounts: [] -## @param sidecars Add additional sidecar containers to the ClickHouse pod(s) -## e.g: -## sidecars: -## - name: your-image-name -## image: your-image -## imagePullPolicy: Always -## ports: -## - name: portname -## containerPort: 1234 -## -sidecars: [] -## @param initContainers Add additional init containers to the ClickHouse pod(s) -## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ -## e.g: -## initContainers: -## - name: your-image-name -## image: your-image -## imagePullPolicy: Always -## command: ['sh', '-c', 'echo "hello world"'] -## -initContainers: [] - -## TLS configuration -## -tls: - ## @param tls.enabled Enable TLS traffic support - ## - enabled: false - ## @param tls.autoGenerated Generate automatically self-signed TLS certificates - ## - autoGenerated: false - ## @param tls.certificatesSecret Name of an existing secret that contains the certificates - ## - certificatesSecret: "" - ## @param tls.certFilename Certificate filename - ## - certFilename: "" - ## @param tls.certKeyFilename Certificate key filename - ## - certKeyFilename: "" - ## @param tls.certCAFilename CA Certificate filename - ## If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate - ## ref: https://www.postgresql.org/docs/9.6/auth-methods.html - ## - certCAFilename: "" - -## @section Traffic Exposure Parameters -## - -## ClickHouse service parameters -## -service: - ## @param service.type ClickHouse service type - ## - type: ClusterIP - ## @param service.ports.http ClickHouse service HTTP port - ## @param service.ports.https ClickHouse service HTTPS port - ## @param service.ports.tcp ClickHouse service TCP port - ## @param service.ports.tcpSecure ClickHouse service TCP (secure) port - ## @param service.ports.keeper ClickHouse keeper TCP container port - ## @param service.ports.keeperSecure ClickHouse keeper TCP (secure) container port - ## @param service.ports.keeperInter ClickHouse keeper interserver TCP container port - ## @param service.ports.mysql ClickHouse service MySQL port - ## @param service.ports.postgresql ClickHouse service PostgreSQL port - ## @param service.ports.interserver ClickHouse service Interserver port - ## @param service.ports.metrics ClickHouse service metrics port - ## - ports: - http: 8123 - https: 443 - tcp: 9000 - tcpSecure: 9440 - keeper: 2181 - keeperSecure: 3181 - keeperInter: 9444 - mysql: 9004 - postgresql: 9005 - interserver: 9009 - metrics: 8001 - ## Node ports to expose - ## @param service.nodePorts.http Node port for HTTP - ## @param service.nodePorts.https Node port for HTTPS - ## @param service.nodePorts.tcp Node port for TCP - ## @param service.nodePorts.tcpSecure Node port for TCP (with TLS) - ## @param service.nodePorts.keeper ClickHouse keeper TCP container port - ## @param service.nodePorts.keeperSecure ClickHouse keeper TCP (secure) container port - ## @param service.nodePorts.keeperInter ClickHouse keeper interserver TCP container port - ## @param service.nodePorts.mysql Node port for MySQL - ## @param service.nodePorts.postgresql Node port for PostgreSQL - ## @param service.nodePorts.interserver Node port for Interserver - ## @param service.nodePorts.metrics Node port for metrics - ## NOTE: choose port between <30000-32767> - ## - nodePorts: - http: "" - https: "" - tcp: "" - tcpSecure: "" - keeper: "" - keeperSecure: "" - keeperInter: "" - mysql: "" - postgresql: "" - interserver: "" - metrics: "" - ## @param service.clusterIP ClickHouse service Cluster IP - ## e.g.: - ## clusterIP: None - ## - clusterIP: "" - ## @param service.loadBalancerIP ClickHouse service Load Balancer IP - ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer - ## - loadBalancerIP: "" - ## @param service.loadBalancerSourceRanges ClickHouse service Load Balancer sources - ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service - ## e.g: - ## loadBalancerSourceRanges: - ## - 10.10.10.0/24 - ## - loadBalancerSourceRanges: [] - ## @param service.externalTrafficPolicy ClickHouse service external traffic policy - ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip - ## - externalTrafficPolicy: Cluster - ## @param service.annotations Additional custom annotations for ClickHouse service - ## - annotations: {} - ## @param service.extraPorts Extra ports to expose in ClickHouse service (normally used with the `sidecars` value) - ## - extraPorts: [] - ## @param service.sessionAffinity Control where client requests go, to the same pod or round-robin - ## Values: ClientIP or None - ## ref: https://kubernetes.io/docs/user-guide/services/ - ## - sessionAffinity: None - ## @param service.sessionAffinityConfig Additional settings for the sessionAffinity - ## sessionAffinityConfig: - ## clientIP: - ## timeoutSeconds: 300 - ## - sessionAffinityConfig: {} - ## Headless service properties - ## - headless: - ## @param service.headless.annotations Annotations for the headless service. - ## - annotations: {} - -## External Access to ClickHouse configuration -## -externalAccess: - ## @param externalAccess.enabled Enable Kubernetes external cluster access to ClickHouse - ## - enabled: false - ## Parameters to configure K8s service(s) used to externally access ClickHouse - ## Note: A new service per will be created - ## - service: - ## @param externalAccess.service.type Kubernetes Service type for external access. It can be NodePort, LoadBalancer or ClusterIP - ## - type: LoadBalancer - ## @param externalAccess.service.ports.http ClickHouse service HTTP port - ## @param externalAccess.service.ports.https ClickHouse service HTTPS port - ## @param externalAccess.service.ports.tcp ClickHouse service TCP port - ## @param externalAccess.service.ports.tcpSecure ClickHouse service TCP (secure) port - ## @param externalAccess.service.ports.keeper ClickHouse keeper TCP container port - ## @param externalAccess.service.ports.keeperSecure ClickHouse keeper TCP (secure) container port - ## @param externalAccess.service.ports.keeperInter ClickHouse keeper interserver TCP container port - ## @param externalAccess.service.ports.mysql ClickHouse service MySQL port - ## @param externalAccess.service.ports.postgresql ClickHouse service PostgreSQL port - ## @param externalAccess.service.ports.interserver ClickHouse service Interserver port - ## @param externalAccess.service.ports.metrics ClickHouse service metrics port - ## - ports: - http: 80 - https: 443 - tcp: 9000 - tcpSecure: 9440 - keeper: 2181 - keeperSecure: 3181 - keeperInter: 9444 - mysql: 9004 - postgresql: 9005 - interserver: 9009 - metrics: 8001 - ## @param externalAccess.service.loadBalancerIPs Array of load balancer IPs for each ClickHouse . Length must be the same as replicaCount - ## e.g: - ## loadBalancerIPs: - ## - X.X.X.X - ## - Y.Y.Y.Y - ## - loadBalancerIPs: [] - ## @param externalAccess.service.loadBalancerAnnotations Array of load balancer annotations for each ClickHouse . Length must be the same as shards multiplied by replicaCount - ## e.g: - ## loadBalancerAnnotations: - ## - external-dns.alpha.kubernetes.io/hostname: 1.external.example.com. - ## - external-dns.alpha.kubernetes.io/hostname: 2.external.example.com. - ## - loadBalancerAnnotations: [] - ## @param externalAccess.service.loadBalancerSourceRanges Address(es) that are allowed when service is LoadBalancer - ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service - ## e.g: - ## loadBalancerSourceRanges: - ## - 10.10.10.0/24 - ## - loadBalancerSourceRanges: [] - ## @param externalAccess.service.nodePorts.http Node port for HTTP - ## @param externalAccess.service.nodePorts.https Node port for HTTPS - ## @param externalAccess.service.nodePorts.tcp Node port for TCP - ## @param externalAccess.service.nodePorts.tcpSecure Node port for TCP (with TLS) - ## @param externalAccess.service.nodePorts.keeper ClickHouse keeper TCP container port - ## @param externalAccess.service.nodePorts.keeperSecure ClickHouse keeper TCP container port (with TLS) - ## @param externalAccess.service.nodePorts.keeperInter ClickHouse keeper interserver TCP container port - ## @param externalAccess.service.nodePorts.mysql Node port for MySQL - ## @param externalAccess.service.nodePorts.postgresql Node port for PostgreSQL - ## @param externalAccess.service.nodePorts.interserver Node port for Interserver - ## @param externalAccess.service.nodePorts.metrics Node port for metrics - ## NOTE: choose port between <30000-32767> - ## e.g: - ## nodePorts: - ## tls: - ## - 30001 - ## - 30002 - ## - nodePorts: - http: [] - https: [] - tcp: [] - tcpSecure: [] - keeper: [] - keeperSecure: [] - keeperInter: [] - mysql: [] - postgresql: [] - interserver: [] - metrics: [] - ## @param externalAccess.service.labels Service labels for external access - ## - labels: {} - ## @param externalAccess.service.annotations Service annotations for external access - ## - annotations: {} - ## @param externalAccess.service.extraPorts Extra ports to expose in the ClickHouse external service - ## - extraPorts: [] - -## ClickHouse ingress parameters -## ref: http://kubernetes.io/docs/user-guide/ingress/ -## -ingress: - ## @param ingress.enabled Enable ingress record generation for ClickHouse - ## - enabled: false - ## @param ingress.pathType Ingress path type - ## - pathType: ImplementationSpecific - ## @param ingress.apiVersion Force Ingress API version (automatically detected if not set) - ## - apiVersion: "" - ## @param ingress.hostname Default host for the ingress record - ## - hostname: clickhouse.local - ## @param ingress.ingressClassName IngressClass that will be be used to implement the Ingress (Kubernetes 1.18+) - ## This is supported in Kubernetes 1.18+ and required if you have more than one IngressClass marked as the default for your cluster . - ## ref: https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/ - ## - ingressClassName: "" - ## @param ingress.path Default path for the ingress record - ## NOTE: You may need to set this to '/*' in order to use this with ALB ingress controllers - ## - path: / - ## @param ingress.annotations Additional annotations for the Ingress resource. To enable certificate autogeneration, place here your cert-manager annotations. - ## Use this parameter to set the required annotations for cert-manager, see - ## ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations - ## e.g: - ## annotations: - ## kubernetes.io/ingress.class: nginx - ## cert-manager.io/cluster-issuer: cluster-issuer-name - ## - annotations: {} - ## @param ingress.tls Enable TLS configuration for the host defined at `ingress.hostname` parameter - ## TLS certificates will be retrieved from a TLS secret with name: `{{- printf "%s-tls" .Values.ingress.hostname }}` - ## You can: - ## - Use the `ingress.secrets` parameter to create this TLS secret - ## - Rely on cert-manager to create it by setting the corresponding annotations - ## - Rely on Helm to create self-signed certificates by setting `ingress.selfSigned=true` - ## - tls: false - ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm - ## - selfSigned: false - ## @param ingress.extraHosts An array with additional hostname(s) to be covered with the ingress record - ## e.g: - ## extraHosts: - ## - name: clickhouse.local - ## path: / - ## - extraHosts: [] - ## @param ingress.extraPaths An array with additional arbitrary paths that may need to be added to the ingress under the main host - ## e.g: - ## extraPaths: - ## - path: /* - ## backend: - ## serviceName: ssl-redirect - ## servicePort: use-annotation - ## - extraPaths: [] - ## @param ingress.extraTls TLS configuration for additional hostname(s) to be covered with this ingress record - ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls - ## e.g: - ## extraTls: - ## - hosts: - ## - clickhouse.local - ## secretName: clickhouse.local-tls - ## - extraTls: [] - ## @param ingress.secrets Custom TLS certificates as secrets - ## NOTE: 'key' and 'certificate' are expected in PEM format - ## NOTE: 'name' should line up with a 'secretName' set further up - ## If it is not set and you're using cert-manager, this is unneeded, as it will create a secret for you with valid certificates - ## If it is not set and you're NOT using cert-manager either, self-signed certificates will be created valid for 365 days - ## It is also possible to create and manage the certificates outside of this helm chart - ## Please see README.md for more information - ## e.g: - ## secrets: - ## - name: clickhouse.local-tls - ## key: |- - ## -----BEGIN RSA PRIVATE KEY----- - ## ... - ## -----END RSA PRIVATE KEY----- - ## certificate: |- - ## -----BEGIN CERTIFICATE----- - ## ... - ## -----END CERTIFICATE----- - ## - secrets: [] - ## @param ingress.extraRules Additional rules to be covered with this ingress record - ## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-rules - ## e.g: - ## extraRules: - ## - host: example.local - ## http: - ## path: / - ## backend: - ## service: - ## name: example-svc - ## port: - ## name: http - ## - extraRules: [] - -## @section Persistence Parameters -## - -## Enable persistence using Persistent Volume Claims -## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ -## -persistence: - ## @param persistence.enabled Enable persistence using Persistent Volume Claims - ## - enabled: true - ## @param persistence.existingClaim Name of an existing PVC to use - ## - existingClaim: "" - ## @param persistence.storageClass Storage class of backing PVC - ## If defined, storageClassName: - ## If set to "-", storageClassName: "", which disables dynamic provisioning - ## If undefined (the default) or set to null, no storageClassName spec is - ## set, choosing the default provisioner. (gp2 on AWS, standard on - ## GKE, AWS & OpenStack) - ## - storageClass: "" - ## @param persistence.labels Persistent Volume Claim labels - ## - labels: {} - ## @param persistence.annotations Persistent Volume Claim annotations - ## - annotations: {} - ## @param persistence.accessModes Persistent Volume Access Modes - ## - accessModes: - - ReadWriteOnce - ## @param persistence.size Size of data volume - ## - size: 15Gi - ## @param persistence.selector Selector to match an existing Persistent Volume for ClickHouse data PVC - ## If set, the PVC can't have a PV dynamically provisioned for it - ## E.g. - ## selector: - ## matchLabels: - ## app: my-app - ## - selector: {} - ## @param persistence.dataSource Custom PVC data source - ## - dataSource: {} -## @section Init Container Parameters -## - -## 'volumePermissions' init container parameters -## Changes the owner and group of the persistent volume mount point to runAsUser:fsGroup values -## based on the *podSecurityContext/*containerSecurityContext parameters -## -volumePermissions: - ## @param volumePermissions.enabled Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` - ## - enabled: false - ## OS Shell + Utility image - ## ref: https://hub.docker.com/r/bitnami/os-shell/tags/ - ## @param volumePermissions.image.registry [default: REGISTRY_NAME] OS Shell + Utility image registry - ## @param volumePermissions.image.repository [default: REPOSITORY_NAME/os-shell] OS Shell + Utility image repository - ## @skip volumePermissions.image.tag OS Shell + Utility image tag (immutable tags are recommended) - ## @param volumePermissions.image.pullPolicy OS Shell + Utility image pull policy - ## @param volumePermissions.image.pullSecrets OS Shell + Utility image pull secrets - ## - image: - registry: docker.io - repository: bitnami/os-shell - tag: 11-debian-11-r91 - pullPolicy: IfNotPresent - ## Optionally specify an array of imagePullSecrets. - ## Secrets must be manually created in the namespace. - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - ## e.g: - ## pullSecrets: - ## - myRegistryKeySecretName - ## - pullSecrets: [] - ## Init container's resource requests and limits - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - ## @param volumePermissions.resources.limits The resources limits for the init container - ## @param volumePermissions.resources.requests The requested resources for the init container - ## - resources: - limits: {} - requests: {} - ## Init container Container Security Context - ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container - ## @param volumePermissions.containerSecurityContext.runAsUser Set init container's Security Context runAsUser - ## NOTE: when runAsUser is set to special value "auto", init container will try to chown the - ## data folder to auto-determined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` - ## "auto" is especially useful for OpenShift which has scc with dynamic user ids (and 0 is not allowed) - ## - containerSecurityContext: - runAsUser: 0 - -## @section Other Parameters -## - -## ServiceAccount configuration -## -serviceAccount: - ## @param serviceAccount.create Specifies whether a ServiceAccount should be created - ## - create: true - ## @param serviceAccount.name The name of the ServiceAccount to use. - ## If not set and create is true, a name is generated using the common.names.fullname template - ## - name: "" - ## @param serviceAccount.annotations Additional Service Account annotations (evaluated as a template) - ## - annotations: {} - ## @param serviceAccount.automountServiceAccountToken Automount service account token for the server service account - ## - automountServiceAccountToken: true - -## Prometheus metrics -## -metrics: - ## @param metrics.enabled Enable the export of Prometheus metrics - ## - enabled: false - ## @param metrics.podAnnotations [object] Annotations for metrics scraping - ## - podAnnotations: - prometheus.io/scrape: "true" - prometheus.io/port: "{{ .Values.containerPorts.metrics }}" - ## Prometheus Operator ServiceMonitor configuration - ## - serviceMonitor: - ## @param metrics.serviceMonitor.enabled if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) - ## - enabled: false - ## @param metrics.serviceMonitor.namespace Namespace in which Prometheus is running - ## - namespace: "" - ## @param metrics.serviceMonitor.annotations Additional custom annotations for the ServiceMonitor - ## - annotations: {} - ## @param metrics.serviceMonitor.labels Extra labels for the ServiceMonitor - ## - labels: {} - ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in Prometheus - ## - jobLabel: "" - ## @param metrics.serviceMonitor.honorLabels honorLabels chooses the metric's labels on collisions with target labels - ## - honorLabels: false - ## @param metrics.serviceMonitor.interval Interval at which metrics should be scraped. - ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint - ## e.g: - ## interval: 10s - ## - interval: "" - ## @param metrics.serviceMonitor.scrapeTimeout Timeout after which the scrape is ended - ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint - ## e.g: - ## scrapeTimeout: 10s - ## - scrapeTimeout: "" - ## @param metrics.serviceMonitor.metricRelabelings Specify additional relabeling of metrics - ## - metricRelabelings: [] - ## @param metrics.serviceMonitor.relabelings Specify general relabeling - ## - relabelings: [] - ## @param metrics.serviceMonitor.selector Prometheus instance selector labels - ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration - ## selector: - ## prometheus: my-prometheus - ## - selector: {} - - ## Prometheus Operator PrometheusRule configuration - ## - prometheusRule: - ## @param metrics.prometheusRule.enabled Create a PrometheusRule for Prometheus Operator - ## - enabled: false - ## @param metrics.prometheusRule.namespace Namespace for the PrometheusRule Resource (defaults to the Release Namespace) - ## - namespace: "" - ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so PrometheusRule will be discovered by Prometheus - ## - additionalLabels: {} - ## @param metrics.prometheusRule.rules PrometheusRule definitions - ## - alert: ClickhouseServerRestart - ## annotations: - ## message: Clickhouse-server started recently - ## expr: ClickHouseAsyncMetrics_Uptime > 1 < 180 - ## for: 5m - ## labels: - ## severity: warning - rules: [] - -## @section External Zookeeper paramaters -## -externalZookeeper: - ## @param externalZookeeper.servers List of external zookeeper servers to use - ## @param externalZookeeper.port Port of the Zookeeper servers - ## - servers: [] - port: 2888 - -## @section Zookeeper subchart parameters -## -## @param zookeeper.enabled Deploy Zookeeper subchart -## @param zookeeper.replicaCount Number of Zookeeper instances -## @param zookeeper.service.ports.client Zookeeper client port -## -zookeeper: - enabled: false - ## Override zookeeper default image as 3.9 is not supported https://github.com/ClickHouse/ClickHouse/issues/53749 - ## ref: https://github.com/bitnami/containers/tree/main/bitnami/zookeeper - ## @param zookeeper.image.registry [default: REGISTRY_NAME] Zookeeper image registry - ## @param zookeeper.image.repository [default: REPOSITORY_NAME/zookeeper] Zookeeper image repository - ## @skip zookeeper.image.tag Zookeeper image tag (immutable tags are recommended) - ## @param zookeeper.image.pullPolicy Zookeeper image pull policy - image: - registry: docker.io - repository: bitnami/zookeeper - tag: 3.8.3-debian-11-r2 - pullPolicy: IfNotPresent - replicaCount: 3 - service: - ports: - client: 2181 diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 58ebc2dc..d7a05d6a 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.10 +version: 1.1.11 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to @@ -29,8 +29,8 @@ dependencies: repository: https://intelops.github.io/kubviz/ - name: clickhouse condition: clickhouse.enabled - version: 1.0.2 - repository: https://intelops.github.io/kubviz/ + version: 1.0.0 + repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: grafana condition: grafana.enabled version: 1.0.5 diff --git a/charts/client/templates/configmap-clickhouse-datasource.yaml b/charts/client/templates/configmap-clickhouse-datasource.yaml index ddcc43d9..4052616a 100644 --- a/charts/client/templates/configmap-clickhouse-datasource.yaml +++ b/charts/client/templates/configmap-clickhouse-datasource.yaml @@ -15,10 +15,13 @@ data: port: 9000 {{- if .Values.clickhouse.enabled }} server: {{ include "client.fullname" . }}-clickhouse + tlsSkipVerify: true + username: {{ .Values.clickhouse.user }} + secureJsonData: + password: {{ .Values.clickhouse.password }} {{- else }} server: {{ .Values.existingClickhouse.host }} tlsSkipVerify: true - {{- if not .Values.clickhouse.enabled }} {{- if not .Values.existingClickhouse.secret }} username: {{ .Values.existingClickhouse.username }} {{- else }} @@ -30,6 +33,5 @@ data: {{- else }} password: $CLICKHOUSE_PASSWORD {{- end }} - {{- end }} {{- end }} {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-vertamedia-datasource.yaml b/charts/client/templates/configmap-vertamedia-datasource.yaml index 983a15ee..627f1dc6 100644 --- a/charts/client/templates/configmap-vertamedia-datasource.yaml +++ b/charts/client/templates/configmap-vertamedia-datasource.yaml @@ -13,11 +13,15 @@ data: type: vertamedia-clickhouse-datasource {{- if .Values.clickhouse.enabled }} url: {{ include "client.fullname" . }}-clickhouse:8123 + access: proxy + basicAuth: true + basicAuthUser: {{ .Values.clickhouse.user }} + secureJsonData: + basicAuthPassword: {{ .Values.clickhouse.password }} {{- else }} url: {{ .Values.existingClickhouse.host }}:8123 access: proxy - {{- if not .Values.clickhouse.enabled }} - basicAuth: true + basicAuth: true {{- if not .Values.existingClickhouse.secret }} basicAuthUser: {{ .Values.existingClickhouse.username }} {{- else }} @@ -29,6 +33,5 @@ data: {{- else }} basicAuthPassword: $CLICKHOUSE_PASSWORD {{- end }} - {{- end }} {{- end }} {{- end }} \ No newline at end of file diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index bce647bd..64075db4 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -39,10 +39,15 @@ spec: env: - name: SCHEMA_PATH value : {{ .Values.migration.schema.path }} - - name: DB_ADDRESS {{- if .Values.clickhouse.enabled }} + - name: DB_ADDRESS value: {{ include "client.fullname" . }}-clickhouse + - name: CLICKHOUSE_USERNAME + value: {{ .Values.clickhouse.user }} + - name: CLICKHOUSE_PASSWORD + value: {{ .Values.clickhouse.password }} {{- else }} + - name: DB_ADDRESS value: {{ .Values.existingClickhouse.host }} - name: CLICKHOUSE_USERNAME {{- if not .Values.existingClickhouse.secret }} @@ -99,10 +104,15 @@ spec: {{- end }} - name: NATS_ADDRESS value: {{ include "client.fullname" . }}-nats - - name: DB_ADDRESS {{- if .Values.clickhouse.enabled }} + - name: DB_ADDRESS value: {{ include "client.fullname" . }}-clickhouse + - name: CLICKHOUSE_USERNAME + value: {{ .Values.clickhouse.user }} + - name: CLICKHOUSE_PASSWORD + value: {{ .Values.clickhouse.password }} {{- else }} + - name: DB_ADDRESS value: {{ .Values.existingClickhouse.host }} - name: CLICKHOUSE_USERNAME {{- if not .Values.existingClickhouse.secret }} diff --git a/charts/client/values.yaml b/charts/client/values.yaml index f612cb05..aa5f94b3 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -61,10 +61,11 @@ resources: limits: cpu: 200m memory: 256Mi - ephemeral-storage: 100Mi + ephemeral-storage: 50Mi requests: cpu: 100m memory: 128Mi + ephemeral-storage: 50Mi autoscaling: enabled: false @@ -96,8 +97,9 @@ nats: clickhouse: enabled: true - clickhouse: - replicas: "1" + user: admin + password: admin + replicasCount: 1 existingClickhouse: host: clickhouse From 59568dc7dea8c44e08d41b1a7d79a0db25fe4f24 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 11 Dec 2023 13:19:35 +0530 Subject: [PATCH 145/263] Add persistence support in Agent --- .github/workflows/helm_release.yml | 1 - charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 60 ++++++++++++++++++++++++-- charts/agent/templates/pvc.yaml | 13 ++++++ charts/agent/values.yaml | 31 +++++++++++-- 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 charts/agent/templates/pvc.yaml diff --git a/.github/workflows/helm_release.yml b/.github/workflows/helm_release.yml index f16b4250..597ddaf8 100644 --- a/.github/workflows/helm_release.yml +++ b/.github/workflows/helm_release.yml @@ -23,7 +23,6 @@ jobs: - name: Add Helm repos run: | helm repo add tools https://kube-tarian.github.io/helmrepo-supporting-tools - helm repo add bitnami https://charts.bitnami.com/bitnami - name: Run chart-releaser uses: helm/chart-releaser-action@v1.1.0 diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 9133af69..dfadb805 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.7 +version: 1.1.8 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index 1ce92e1d..05a8abd3 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -75,8 +75,24 @@ spec: value: "{{ .Values.schedule.kubepreupgradeInterval }}" - name: TRIVY_INTERVAL value: "{{ .Values.schedule.trivyInterval }}" + {{- if .Values.persistence.enabled }} + volumeMounts: + - name: data + mountPath: {{ .Values.persistence.mountPath }} + {{- end }} resources: - {{- toYaml .Values.resources | nindent 12 }} + limits: + cpu: {{ .Values.resources.limits.cpu }} + memory: {{ .Values.resources.limits.memory }} + {{- if not .Values.persistence.enabled }} + ephemeral-storage: {{ .Values.resources.limits.ephemeralstorage }} + {{- end }} + requests: + cpu: {{ .Values.resources.requests.cpu }} + memory: {{ .Values.resources.requests.memory }} + {{- if not .Values.persistence.enabled }} + ephemeral-storage: {{ .Values.resources.requests.ephemeralstorage }} + {{- end }} {{- if .Values.git_bridge.enabled }} - name: git-bridge image: "{{ .Values.git_bridge.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" @@ -99,8 +115,24 @@ spec: {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} + {{- if .Values.git_bridge.persistence.enabled }} + volumeMounts: + - name: data + mountPath: {{ .Values.git_bridge.persistence.mountPath }} + {{- end }} resources: - {{- toYaml .Values.git_bridge.resources | nindent 12 }} + limits: + cpu: {{ .Values.git_bridge.resources.limits.cpu }} + memory: {{ .Values.git_bridge.resources.limits.memory }} + {{- if not .Values.git_bridge.persistence.enabled }} + ephemeral-storage: {{ .Values.git_bridge.resources.limits.ephemeralstorage }} + {{- end }} + requests: + cpu: {{ .Values.git_bridge.resources.requests.cpu }} + memory: {{ .Values.git_bridge.resources.requests.memory }} + {{- if not .Values.git_bridge.persistence.enabled }} + ephemeral-storage: {{ .Values.git_bridge.resources.requests.ephemeralstorage }} + {{- end }} {{- end }} {{- if .Values.container_bridge.enabled }} - name: container-bridge @@ -124,9 +156,31 @@ spec: {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} + {{- if .Values.container_bridge.persistence.enabled }} + volumeMounts: + - name: data + mountPath: {{ .Values.container_bridge.persistence.mountPath }} + {{- end }} resources: - {{- toYaml .Values.container_bridge.resources | nindent 12 }} + limits: + cpu: {{ .Values.container_bridge.resources.limits.cpu }} + memory: {{ .Values.container_bridge.resources.limits.memory }} + {{- if not .Values.container_bridge.persistence.enabled }} + ephemeral-storage: {{ .Values.container_bridge.resources.limits.ephemeralstorage }} + {{- end }} + requests: + cpu: {{ .Values.container_bridge.resources.requests.cpu }} + memory: {{ .Values.container_bridge.resources.requests.memory }} + {{- if not .Values.container_bridge.persistence.enabled }} + ephemeral-storage: {{ .Values.container_bridge.resources.requests.ephemeralstorage }} + {{- end }} {{- end }} + {{- if .Values.persistence.enabled }} + volumes: + - name: data + persistentVolumeClaim: + claimName: {{ include "agent.fullname" . }}-data + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/charts/agent/templates/pvc.yaml b/charts/agent/templates/pvc.yaml new file mode 100644 index 00000000..920c3dab --- /dev/null +++ b/charts/agent/templates/pvc.yaml @@ -0,0 +1,13 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "agent.fullname" . }}-data +spec: + accessModes: + - {{ .Values.persistence.accessMode }} + storageClassName: {{ .Values.persistence.storageClass | default "" }} + resources: + requests: + storage: {{ .Values.persistence.size }} +{{- end }} \ No newline at end of file diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 7613d4ca..48aa6710 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -52,10 +52,18 @@ git_bridge: limits: cpu: 200m memory: 256Mi - ephemeral-storage: 100Mi + ephemeralstorage: 100Mi requests: cpu: 200m memory: 256Mi + ephemeralstorage: 100Mi + persistence: + enabled: true + existingClaim: "" + storageClass: "" + mountPath: /mnt/agent/gb + accessMode: ReadWriteOnce + size: 5Gi ingress: enabled: true annotations: @@ -87,10 +95,18 @@ container_bridge: limits: cpu: 200m memory: 256Mi - ephemeral-storage: 100Mi + ephemeralstorage: 100Mi requests: cpu: 200m memory: 256Mi + ephemeralstorage: 100Mi + persistence: + enabled: true + existingClaim: "" + storageClass: "" + mountPath: /mnt/agent/cb + accessMode: ReadWriteOnce + size: 5Gi ingress: enabled: true annotations: @@ -127,10 +143,19 @@ resources: limits: cpu: 2 memory: 2Gi - ephemeral-storage: 1Gi + ephemeralstorage: 1Gi requests: cpu: 200m memory: 256Mi + ephemeralstorage: 256Mi + +persistence: + enabled: true + existingClaim: "" + storageClass: "" + mountPath: /mnt/agent/kbz + accessMode: ReadWriteOnce + size: 5Gi autoscaling: enabled: false From feb74d39dd71cf29a0b205becfe70c9c1798672e Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 18 Dec 2023 11:17:41 +0530 Subject: [PATCH 146/263] updated nats dependency in client chart --- charts/client/Chart.yaml | 6 +- charts/nats/.helmignore | 22 - charts/nats/Chart.yaml | 22 - charts/nats/README.md | 799 ------------------ charts/nats/templates/NOTES.txt | 26 - charts/nats/templates/_helpers.tpl | 147 ---- charts/nats/templates/configmap.yaml | 544 ------------ charts/nats/templates/nats-box.yaml | 117 --- charts/nats/templates/networkpolicy.yaml | 80 -- charts/nats/templates/pdb.yaml | 21 - charts/nats/templates/rbac.yaml | 31 - charts/nats/templates/service.yaml | 74 -- charts/nats/templates/serviceExternal.yaml | 74 -- charts/nats/templates/serviceMonitor.yaml | 40 - charts/nats/templates/statefulset.yaml | 642 -------------- .../templates/tests/test-request-reply.yaml | 30 - charts/nats/values.yaml | 682 --------------- 17 files changed, 3 insertions(+), 3354 deletions(-) delete mode 100644 charts/nats/.helmignore delete mode 100644 charts/nats/Chart.yaml delete mode 100644 charts/nats/README.md delete mode 100644 charts/nats/templates/NOTES.txt delete mode 100644 charts/nats/templates/_helpers.tpl delete mode 100644 charts/nats/templates/configmap.yaml delete mode 100644 charts/nats/templates/nats-box.yaml delete mode 100644 charts/nats/templates/networkpolicy.yaml delete mode 100644 charts/nats/templates/pdb.yaml delete mode 100644 charts/nats/templates/rbac.yaml delete mode 100644 charts/nats/templates/service.yaml delete mode 100644 charts/nats/templates/serviceExternal.yaml delete mode 100644 charts/nats/templates/serviceMonitor.yaml delete mode 100644 charts/nats/templates/statefulset.yaml delete mode 100644 charts/nats/templates/tests/test-request-reply.yaml delete mode 100644 charts/nats/values.yaml diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index d7a05d6a..ff52050e 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.11 +version: 1.1.12 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to @@ -25,8 +25,8 @@ appVersion: "v1.0.0" dependencies: - name: nats condition: nats.enabled - version: 0.13.5 - repository: https://intelops.github.io/kubviz/ + version: 1.0.0 + repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: clickhouse condition: clickhouse.enabled version: 1.0.0 diff --git a/charts/nats/.helmignore b/charts/nats/.helmignore deleted file mode 100644 index 50af0317..00000000 --- a/charts/nats/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/charts/nats/Chart.yaml b/charts/nats/Chart.yaml deleted file mode 100644 index ae0e67fa..00000000 --- a/charts/nats/Chart.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v2 -appVersion: 2.7.2 -description: A Helm chart for the NATS.io High Speed Cloud Native Distributed Communications - Technology. -home: http://github.com/nats-io/k8s -icon: https://nats.io/img/nats-icon-color.png -keywords: -- nats -- messaging -- cncf -maintainers: -- email: wally@nats.io - name: Waldemar Quevedo - url: https://github.com/wallyqs -- email: colin@nats.io - name: Colin Sullivan - url: https://github.com/ColinSullivan1 -- email: jaime@nats.io - name: Jaime Piña - url: https://github.com/variadico -name: nats -version: 0.13.5 diff --git a/charts/nats/README.md b/charts/nats/README.md deleted file mode 100644 index 4b4bd917..00000000 --- a/charts/nats/README.md +++ /dev/null @@ -1,799 +0,0 @@ -# NATS Server - -[NATS](https://nats.io) is a simple, secure and performant communications system for digital systems, services and devices. NATS is part of the Cloud Native Computing Foundation ([CNCF](https://cncf.io)). NATS has over [30 client language implementations](https://nats.io/download/), and its server can run on-premise, in the cloud, at the edge, and even on a Raspberry Pi. NATS can secure and simplify design and operation of modern distributed systems. - -## TL;DR; - -```console -helm repo add nats https://nats-io.github.io/k8s/helm/charts/ -helm install my-nats nats/nats -``` - -## Configuration - -### Server Image - -```yaml -nats: - image: nats:2.6.5-alpine - pullPolicy: IfNotPresent -``` - -### Limits - -```yaml -nats: - # The number of connect attempts against discovered routes. - connectRetries: 30 - - # How many seconds should pass before sending a PING - # to a client that has no activity. - pingInterval: - - # Server settings. - limits: - maxConnections: - maxSubscriptions: - maxControlLine: - maxPayload: - - writeDeadline: - maxPending: - maxPings: - lameDuckDuration: - - # Number of seconds to wait for client connections to end after the pod termination is requested - terminationGracePeriodSeconds: 60 -``` - -### Logging - -*Note*: It is not recommended to enable trace or debug in production since enabling it will significantly degrade performance. - -```yaml -nats: - logging: - debug: - trace: - logtime: - connectErrorReports: - reconnectErrorReports: -``` - -### TLS setup for client connections - -You can find more on how to setup and trouble shoot TLS connnections at: -https://docs.nats.io/nats-server/configuration/securing_nats/tls - -```yaml -nats: - tls: - secret: - name: nats-client-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" -``` - -## Clustering - -If clustering is enabled, then a 3-node cluster will be setup. More info at: -https://docs.nats.io/nats-server/configuration/clustering#nats-server-clustering - -```yaml -cluster: - enabled: true - replicas: 3 - - tls: - secret: - name: nats-server-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" -``` - -Example: - -```sh -$ helm install nats nats/nats --set cluster.enabled=true -``` - -## Leafnodes - -Leafnode connections to extend a cluster. More info at: -https://docs.nats.io/nats-server/configuration/leafnodes - -```yaml -leafnodes: - enabled: true - remotes: - - url: "tls://connect.ngs.global:7422" - # credentials: - # secret: - # name: leafnode-creds - # key: TA.creds - # tls: - # secret: - # name: nats-leafnode-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - tls: - secret: - name: nats-client-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" -``` - -## Setting up External Access - -### Using HostPorts - -In case of both external access and advertisements being enabled, an -initializer container will be used to gather the public ips. This -container will required to have enough RBAC policy to be able to make a -look up of the public ip of the node where it is running. - -For example, to setup external access for a cluster and advertise the public ip to clients: - -```yaml -nats: - # Toggle whether to enable external access. - # This binds a host port for clients, gateways and leafnodes. - externalAccess: true - - # Toggle to disable client advertisements (connect_urls), - # in case of running behind a load balancer (which is not recommended) - # it might be required to disable advertisements. - advertise: true - - # In case both external access and advertise are enabled - # then a service account would be required to be able to - # gather the public ip from a node. - serviceAccount: "nats-server" -``` - -Where the service account named `nats-server` has the following RBAC policy for example: - -```yaml ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: nats-server - namespace: default ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: nats-server -rules: -- apiGroups: [""] - resources: - - nodes - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: nats-server-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: nats-server -subjects: -- kind: ServiceAccount - name: nats-server - namespace: default -``` - -The container image of the initializer can be customized via: - -```yaml -bootconfig: - image: natsio/nats-boot-config:latest - pullPolicy: IfNotPresent -``` - -### Using LoadBalancers - -In case of using a load balancer for external access, it is recommended to disable no advertise -so that internal ips from the NATS Servers are not advertised to the clients connecting through -the load balancer. - -```yaml -nats: - image: nats:alpine - -cluster: - enabled: true - noAdvertise: true - -leafnodes: - enabled: true - noAdvertise: true - -natsbox: - enabled: true -``` - -Then could use an L4 enabled load balancer to connect to NATS, for example: - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: nats-lb -spec: - type: LoadBalancer - selector: - app.kubernetes.io/name: nats - ports: - - protocol: TCP - port: 4222 - targetPort: 4222 - name: nats - - protocol: TCP - port: 7422 - targetPort: 7422 - name: leafnodes - - protocol: TCP - port: 7522 - targetPort: 7522 - name: gateways -``` - -## Gateways - -A super cluster can be formed by pointing to remote gateways. -You can find more about gateways in the NATS documentation: -https://docs.nats.io/nats-server/configuration/gateways - -```yaml -gateway: - enabled: false - name: 'default' - - ############################# - # # - # List of remote gateways # - # # - ############################# - # gateways: - # - name: other - # url: nats://my-gateway-url:7522 - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - # tls: - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" -``` - -## Auth setup - -### Auth with a Memory Resolver - -```yaml -auth: - enabled: true - - # Reference to the Operator JWT. - operatorjwt: - configMap: - name: operator-jwt - key: KO.jwt - - # Public key of the System Account - systemAccount: - - resolver: - ############################ - # # - # Memory resolver settings # - # # - ############################## - type: memory - - # - # Use a configmap reference which will be mounted - # into the container. - # - configMap: - name: nats-accounts - key: resolver.conf -``` - -### Auth using an Account Server Resolver - -```yaml -auth: - enabled: true - - # Reference to the Operator JWT. - operatorjwt: - configMap: - name: operator-jwt - key: KO.jwt - - # Public key of the System Account - systemAccount: - - resolver: - ########################## - # # - # URL resolver settings # - # # - ########################## - type: URL - url: "http://nats-account-server:9090/jwt/v1/accounts/" -``` - -## JetStream - -### Setting up Memory and File Storage - -```yaml -nats: - image: nats:alpine - - jetstream: - enabled: true - - memStorage: - enabled: true - size: 2Gi - - fileStorage: - enabled: true - size: 1Gi - storageDirectory: /data/ - storageClassName: default -``` - -### Using with an existing PersistentVolumeClaim - -For example, given the following `PersistentVolumeClaim`: - -```yaml ---- -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: nats-js-disk - annotations: - volume.beta.kubernetes.io/storage-class: "default" -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 3Gi -``` - -You can start JetStream so that one pod is bounded to it: - -```yaml -nats: - image: nats:alpine - - jetstream: - enabled: true - - fileStorage: - enabled: true - storageDirectory: /data/ - existingClaim: nats-js-disk - claimStorageSize: 3Gi -``` - -### Clustering example - -```yaml - -nats: - image: nats:alpine - - jetstream: - enabled: true - - memStorage: - enabled: true - size: "2Gi" - - fileStorage: - enabled: true - size: "1Gi" - storageDirectory: /data/ - storageClassName: default - -cluster: - enabled: true - # Cluster name is required, by default will be release name. - # name: "nats" - replicas: 3 -``` - -### Basic Authentication and JetStream - -```yaml -nats: - image: nats:alpine - - jetstream: - enabled: true - - memStorage: - enabled: true - size: "2Gi" - - fileStorage: - enabled: true - size: "8Gi" - storageDirectory: /data/ - storageClassName: gp2 - -cluster: - enabled: true - # Can set a custom cluster name - # name: "nats" - replicas: 3 - -auth: - enabled: true - - systemAccount: sys - - basic: - accounts: - sys: - users: - - user: sys - pass: sys - js: - jetstream: true - users: - - user: foo -``` - -### NATS Resolver setup example - -As of NATS v2.2, the server now has a built-in NATS resolver of accounts. -The following is an example guide of how to get it configured. - -```sh -# Create a working directory to keep the creds. -mkdir nats-creds -cd nats-creds - -# This just creates some accounts for you to get started. -curl -fSl https://nats-io.github.io/k8s/setup/nsc-setup.sh | sh -source .nsc.env - -# You should have some accounts now, at least the following. -nsc list accounts -+-------------------------------------------------------------------+ -| Accounts | -+--------+----------------------------------------------------------+ -| Name | Public Key | -+--------+----------------------------------------------------------+ -| A | ABJ4OIKBBFCNXZDP25C7EWXCXOVCYYAGBEHFAG7F5XYCOYPHZLNSJYDF | -| B | ACVRK7GFBRQUCB3NEABGQ7XPNED2BSPT27GOX5QBDYW2NOFMQKK755DJ | -| SYS | ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N | -+--------+----------------------------------------------------------+ - -# Now create an account with JetStream support -export account=JS1 -nsc add account --name $account -nsc edit account --name $account --js-disk-storage -1 --js-consumer -1 --js-streams -1 -nsc add user -a $account js-user -``` - -Next, generate the NATS resolver config. This will be used to fill in the values of the YAML in the Helm template. -For example the result of generating this: - -```sh -nsc generate config --sys-account SYS --nats-resolver - -# Operator named KO -operator: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDRlozRlE0WURNTUc1Q1UzU0FUWVlHWUdQUDJaQU1QUzVNRUdNWFdWTUJFWUdIVzc2WEdBIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJLTyIsInN1YiI6Ik9DSVYyUUZKV0lOWlVBVDVUMllKQklSQzNCNkpLTVNaS1FORjVLR1A0TjNLWjRGRkRWQVdZWENMIiwibmF0cyI6eyJ0eXBlIjoib3BlcmF0b3IiLCJ2ZXJzaW9uIjoyfX0.e3gvJ-C1IBznmbUljeT_wbLRl1akv5IGBS3rbxs6mzzTvf3zlqQI4wDKVE8Gvb8qfTX6TIwocClfOqNaN3k3CQ - -# System Account named SYS -system_account: ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N - -resolver_preload: { - ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDR0tWVzJGQUszUE5XQTRBWkhHT083UTdZWUVPQkJYNDZaTU1VSFc1TU5QSUFVSFE0RVRRIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJTWVMiLCJzdWIiOiJBREdGSDROWVY1Vjc1U1ZNNURZU1c1QVdPRDdIMk5SVVdBTU82WExaS0lER1VXWUVYQ1pHNUQ2TiIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwid2lsZGNhcmRzIjp0cnVlLCJjb25uIjotMSwibGVhZiI6LTF9LCJkZWZhdWx0X3Blcm1pc3Npb25zIjp7InB1YiI6e30sInN1YiI6e319LCJ0eXBlIjoiYWNjb3VudCIsInZlcnNpb24iOjJ9fQ.J7g73TEn-ZT13owq4cVWl4l0hZnGK4DJtH2WWOZmGbefcCQ1xsx4cIagKc1cZTCwUpELVAYnSkmPp4LsQOspBg, -} -``` - -In the YAML would be configured as follows: - -``` -auth: - enabled: true - - timeout: "5s" - - resolver: - type: full - - operator: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDRlozRlE0WURNTUc1Q1UzU0FUWVlHWUdQUDJaQU1QUzVNRUdNWFdWTUJFWUdIVzc2WEdBIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJLTyIsInN1YiI6Ik9DSVYyUUZKV0lOWlVBVDVUMllKQklSQzNCNkpLTVNaS1FORjVLR1A0TjNLWjRGRkRWQVdZWENMIiwibmF0cyI6eyJ0eXBlIjoib3BlcmF0b3IiLCJ2ZXJzaW9uIjoyfX0.e3gvJ-C1IBznmbUljeT_wbLRl1akv5IGBS3rbxs6mzzTvf3zlqQI4wDKVE8Gvb8qfTX6TIwocClfOqNaN3k3CQ - - systemAccount: ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N - - store: - dir: "/etc/nats-config/accounts/jwt" - size: "1Gi" - - resolverPreload: - ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDR0tWVzJGQUszUE5XQTRBWkhHT083UTdZWUVPQkJYNDZaTU1VSFc1TU5QSUFVSFE0RVRRIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJTWVMiLCJzdWIiOiJBREdGSDROWVY1Vjc1U1ZNNURZU1c1QVdPRDdIMk5SVVdBTU82WExaS0lER1VXWUVYQ1pHNUQ2TiIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwid2lsZGNhcmRzIjp0cnVlLCJjb25uIjotMSwibGVhZiI6LTF9LCJkZWZhdWx0X3Blcm1pc3Npb25zIjp7InB1YiI6e30sInN1YiI6e319LCJ0eXBlIjoiYWNjb3VudCIsInZlcnNpb24iOjJ9fQ.J7g73TEn-ZT13owq4cVWl4l0hZnGK4DJtH2WWOZmGbefcCQ1xsx4cIagKc1cZTCwUpELVAYnSkmPp4LsQOspBg -``` - -Now we start the server with the NATS Account Resolver (`auth.resolver.type=full`): - -```yaml -nats: - image: nats:2.6.1-alpine - - logging: - debug: false - trace: false - - jetstream: - enabled: true - - memStorage: - enabled: true - size: "2Gi" - - fileStorage: - enabled: true - size: "4Gi" - storageDirectory: /data/ - storageClassName: gp2 # NOTE: AWS setup but customize as needed for your infra. - -cluster: - enabled: true - # Can set a custom cluster name - name: "nats" - replicas: 3 - -auth: - enabled: true - - timeout: "5s" - - resolver: - type: full - - operator: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDRlozRlE0WURNTUc1Q1UzU0FUWVlHWUdQUDJaQU1QUzVNRUdNWFdWTUJFWUdIVzc2WEdBIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJLTyIsInN1YiI6Ik9DSVYyUUZKV0lOWlVBVDVUMllKQklSQzNCNkpLTVNaS1FORjVLR1A0TjNLWjRGRkRWQVdZWENMIiwibmF0cyI6eyJ0eXBlIjoib3BlcmF0b3IiLCJ2ZXJzaW9uIjoyfX0.e3gvJ-C1IBznmbUljeT_wbLRl1akv5IGBS3rbxs6mzzTvf3zlqQI4wDKVE8Gvb8qfTX6TIwocClfOqNaN3k3CQ - - systemAccount: ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N - - store: - dir: "/etc/nats-config/accounts/jwt" - size: "1Gi" - - resolverPreload: - ADGFH4NYV5V75SVM5DYSW5AWOD7H2NRUWAMO6XLZKIDGUWYEXCZG5D6N: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJDR0tWVzJGQUszUE5XQTRBWkhHT083UTdZWUVPQkJYNDZaTU1VSFc1TU5QSUFVSFE0RVRRIiwiaWF0IjoxNjMyNzgzMDk2LCJpc3MiOiJPQ0lWMlFGSldJTlpVQVQ1VDJZSkJJUkMzQjZKS01TWktRTkY1S0dQNE4zS1o0RkZEVkFXWVhDTCIsIm5hbWUiOiJTWVMiLCJzdWIiOiJBREdGSDROWVY1Vjc1U1ZNNURZU1c1QVdPRDdIMk5SVVdBTU82WExaS0lER1VXWUVYQ1pHNUQ2TiIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwid2lsZGNhcmRzIjp0cnVlLCJjb25uIjotMSwibGVhZiI6LTF9LCJkZWZhdWx0X3Blcm1pc3Npb25zIjp7InB1YiI6e30sInN1YiI6e319LCJ0eXBlIjoiYWNjb3VudCIsInZlcnNpb24iOjJ9fQ.J7g73TEn-ZT13owq4cVWl4l0hZnGK4DJtH2WWOZmGbefcCQ1xsx4cIagKc1cZTCwUpELVAYnSkmPp4LsQOspBg -``` - -Finally, using a local port-forward make it possible to establish a connection to one of the servers and upload the accounts. - -```sh -nsc push --system-account SYS -u nats://localhost:4222 -A -[ OK ] push to nats-server "nats://localhost:4222" using system account "SYS": - [ OK ] push JS1 to nats-server with nats account resolver: - [ OK ] pushed "JS1" to nats-server nats-0: jwt updated - [ OK ] pushed "JS1" to nats-server nats-1: jwt updated - [ OK ] pushed "JS1" to nats-server nats-2: jwt updated - [ OK ] pushed to a total of 3 nats-server -``` - -Now you should be able to use JetStream and the NATS based account resolver: - -```sh -nats stream ls -s localhost --creds ./nsc/nkeys/creds/KO/JS1/js-user.creds -No Streams defined -``` - -## Misc - -### NATS Box - -A lightweight container with NATS and NATS Streaming utilities that is deployed along the cluster to confirm the setup. -You can find the image at: https://github.com/nats-io/nats-box - -```yaml -natsbox: - enabled: true - image: natsio/nats-box:latest - pullPolicy: IfNotPresent - - # credentials: - # secret: - # name: nats-sys-creds - # key: sys.creds -``` - -### Configuration Reload sidecar - -The NATS config reloader image to use: - -```yaml -reloader: - enabled: true - image: natsio/nats-server-config-reloader:latest - pullPolicy: IfNotPresent -``` - -### Prometheus Exporter sidecar - -You can toggle whether to start the sidecar that can be used to feed metrics to Prometheus: - -```yaml -exporter: - enabled: true - image: natsio/prometheus-nats-exporter:latest - pullPolicy: IfNotPresent -``` - -### Prometheus operator ServiceMonitor support - -You can enable prometheus operator ServiceMonitor: - -```yaml -exporter: - # You have to enable exporter first - enabled: true - serviceMonitor: - enabled: true - ## Specify the namespace where Prometheus Operator is running - # namespace: monitoring - # ... -``` - -### Pod Customizations - -#### Security Context - -```yaml - # Toggle whether to use setup a Pod Security Context - # ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ -securityContext: - fsGroup: 1000 - runAsUser: 1000 - runAsNonRoot: true -``` - -#### Affinity - - - -`matchExpressions` must be configured according to your setup - -```yaml -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: node.kubernetes.io/purpose - operator: In - values: - - nats - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - nats - - stan - topologyKey: "kubernetes.io/hostname" -``` - -#### Service topology - -[Service topology](https://kubernetes.io/docs/concepts/services-networking/service-topology/) is disabled by default, but can be enabled by setting `topologyKeys`. For example: - -```yaml -topologyKeys: - - "kubernetes.io/hostname" - - "topology.kubernetes.io/zone" - - "topology.kubernetes.io/region" -``` - -#### CPU/Memory Resource Requests/Limits -Sets the pods cpu/memory requests/limits - -```yaml -nats: - resources: - requests: - cpu: 2 - memory: 4Gi - limits: - cpu: 4 - memory: 6Gi -``` - -No resources are set by default. - -#### Annotations - - - -```yaml -podAnnotations: - key1 : "value1", - key2 : "value2" -``` - -### Name Overides - -Can change the name of the resources as needed with: - -```yaml -nameOverride: "my-nats" -``` - -### Image Pull Secrets - -```yaml -imagePullSecrets: -- name: myRegistry -``` - -Adds this to the StatefulSet: - -```yaml -spec: - imagePullSecrets: - - name: myRegistry -``` - -### Mixed TLS and non TLS mode - -You can use the `nats.tls.allowNonTLS` option to allow a cluster to use TLS connections -and plain connections: - -```yaml -nats: - client: - port: 4222 - - tls: - allowNonTLS: true - secret: - name: nats-server-tls - ca: "ca.crt" - cert: "tls.crt" - key: "tls.key" - timeout: "5s" -``` diff --git a/charts/nats/templates/NOTES.txt b/charts/nats/templates/NOTES.txt deleted file mode 100644 index 86661eda..00000000 --- a/charts/nats/templates/NOTES.txt +++ /dev/null @@ -1,26 +0,0 @@ - -{{- if or .Values.nats.logging.debug .Values.nats.logging.trace }} -*WARNING*: Keep in mind that running the server with -debug and/or trace enabled significantly affects the -performance of the server! -{{- end }} - -You can find more information about running NATS on Kubernetes -in the NATS documentation website: - - https://docs.nats.io/nats-on-kubernetes/nats-kubernetes - -{{- if .Values.natsbox.enabled }} - -NATS Box has been deployed into your cluster, you can -now use the NATS tools within the container as follows: - - kubectl exec -n {{ template "nats.namespace" . }} -it deployment/{{ template "nats.fullname" . }}-box -- /bin/sh -l - - nats-box:~# nats-sub test & - nats-box:~# nats-pub test hi - nats-box:~# nc {{ template "nats.fullname" . }} {{ .Values.nats.client.port }} - -{{- end }} - -Thanks for using NATS! diff --git a/charts/nats/templates/_helpers.tpl b/charts/nats/templates/_helpers.tpl deleted file mode 100644 index 5499b6a5..00000000 --- a/charts/nats/templates/_helpers.tpl +++ /dev/null @@ -1,147 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "nats.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "nats.namespace" -}} -{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - - -{{- define "nats.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "nats.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "nats.labels" -}} -helm.sh/chart: {{ include "nats.chart" . }} -{{- range $name, $value := .Values.commonLabels }} -{{ $name }}: {{ tpl $value $ }} -{{- end }} -{{ include "nats.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "nats.selectorLabels" -}} -{{- if .Values.nats.selectorLabels }} -{{ tpl (toYaml .Values.nats.selectorLabels) . }} -{{- else }} -app.kubernetes.io/name: {{ include "nats.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} -{{- end }} - - -{{/* -Return the proper NATS image name -*/}} -{{- define "nats.clusterAdvertise" -}} -{{- if $.Values.useFQDN }} -{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE).svc.%s" (include "nats.fullname" . ) $.Values.k8sClusterDomain }} -{{- else }} -{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE)" (include "nats.fullname" . ) }} -{{- end }} -{{- end }} - -{{/* -Return the NATS cluster routes. -*/}} -{{- define "nats.clusterRoutes" -}} -{{- $name := (include "nats.fullname" . ) -}} -{{- $namespace := (include "nats.namespace" . ) -}} -{{- range $i, $e := until (.Values.cluster.replicas | int) -}} -{{- if $.Values.useFQDN }} -{{- printf "nats://%s-%d.%s.%s.svc.%s:6222," $name $i $name $namespace $.Values.k8sClusterDomain -}} -{{- else }} -{{- printf "nats://%s-%d.%s.%s:6222," $name $i $name $namespace -}} -{{- end }} -{{- end -}} -{{- end }} - -{{- define "nats.extraRoutes" -}} -{{- range $i, $url := .Values.cluster.extraRoutes -}} -{{- printf "%s," $url -}} -{{- end -}} -{{- end }} - -{{- define "nats.tlsConfig" -}} -tls { -{{- if .cert }} - cert_file: {{ .secretPath }}/{{ .secret.name }}/{{ .cert }} -{{- end }} -{{- if .key }} - key_file: {{ .secretPath }}/{{ .secret.name }}/{{ .key }} -{{- end }} -{{- if .ca }} - ca_file: {{ .secretPath }}/{{ .secret.name }}/{{ .ca }} -{{- end }} -{{- if .insecure }} - insecure: {{ .insecure }} -{{- end }} -{{- if .verify }} - verify: {{ .verify }} -{{- end }} -{{- if .verifyAndMap }} - verify_and_map: {{ .verifyAndMap }} -{{- end }} -{{- if .curvePreferences }} - curve_preferences: {{ .curvePreferences }} -{{- end }} -{{- if .timeout }} - timeout: {{ .timeout }} -{{- end }} -{{- if .cipherSuites }} - cipher_suites: {{ toRawJson .cipherSuites }} -{{- end }} -} -{{- end }} - -{{/* -Return the appropriate apiVersion for networkpolicy. -*/}} -{{- define "networkPolicy.apiVersion" -}} -{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} -{{- print "extensions/v1beta1" -}} -{{- else -}} -{{- print "networking.k8s.io/v1" -}} -{{- end -}} -{{- end -}} - -{{/* -Renders a value that contains template. -Usage: -{{ include "tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} -*/}} -{{- define "tplvalues.render" -}} - {{- if typeIs "string" .value }} - {{- tpl .value .context }} - {{- else }} - {{- tpl (.value | toYaml) .context }} - {{- end }} -{{- end -}} diff --git a/charts/nats/templates/configmap.yaml b/charts/nats/templates/configmap.yaml deleted file mode 100644 index 27c0ac37..00000000 --- a/charts/nats/templates/configmap.yaml +++ /dev/null @@ -1,544 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "nats.fullname" . }}-config - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} -data: - nats.conf: | - # NATS Clients Port - port: {{ .Values.nats.client.port }} - - # PID file shared with configuration reloader. - pid_file: "/var/run/nats/nats.pid" - - {{- if .Values.nats.config }} - ########### - # # - # Imports # - # # - ########### - {{- range .Values.nats.config }} - include ./{{ .name }}/{{ .name }}.conf - {{- end}} - {{- end }} - - ############### - # # - # Monitoring # - # # - ############### - http: 8222 - server_name: {{- if .Values.nats.serverNamePrefix }}$SERVER_NAME{{- else }}$POD_NAME{{- end }} - - {{- if .Values.nats.tls }} - ##################### - # # - # TLS Configuration # - # # - ##################### - {{- with .Values.nats.tls }} - {{- $nats_tls := merge (dict) . }} - {{- $_ := set $nats_tls "secretPath" "/etc/nats-certs/clients" }} - {{- tpl (include "nats.tlsConfig" $nats_tls) $ | nindent 4}} - {{- end }} - - {{- if .Values.nats.tls.allowNonTLS }} - allow_non_tls: {{ .Values.nats.tls.allowNonTLS }} - {{- end }} - - {{- end }} - - {{- if .Values.nats.jetstream.enabled }} - ################################### - # # - # NATS JetStream # - # # - ################################### - jetstream { - {{- if .Values.nats.jetstream.encryption }} - {{- if .Values.nats.jetstream.encryption.key }} - key: {{ .Values.nats.jetstream.encryption.key | quote }} - {{- else if .Values.nats.jetstream.encryption.secret }} - key: $JS_KEY - {{- end}} - {{- end}} - - {{- if .Values.nats.jetstream.memStorage.enabled }} - max_mem: {{ .Values.nats.jetstream.memStorage.size }} - {{- end }} - - {{- if .Values.nats.jetstream.domain }} - domain: {{ .Values.nats.jetstream.domain }} - {{- end }} - - {{- if .Values.nats.jetstream.fileStorage.enabled }} - store_dir: {{ .Values.nats.jetstream.fileStorage.storageDirectory }} - - max_file: - {{- if .Values.nats.jetstream.fileStorage.existingClaim }} - {{- .Values.nats.jetstream.fileStorage.claimStorageSize }} - {{- else }} - {{- .Values.nats.jetstream.fileStorage.size }} - {{- end }} - {{- end }} - } - {{- end }} - {{- if .Values.mqtt.enabled }} - ################################### - # # - # NATS MQTT # - # # - ################################### - mqtt { - port: 1883 - - {{- with .Values.mqtt.tls }} - {{- $mqtt_tls := merge (dict) . }} - {{- $_ := set $mqtt_tls "secretPath" "/etc/nats-certs/mqtt" }} - {{- tpl (include "nats.tlsConfig" $mqtt_tls) $ | nindent 6}} - {{- end }} - - {{- if .Values.mqtt.noAuthUser }} - no_auth_user: {{ .Values.mqtt.noAuthUser | quote }} - {{- end }} - - ack_wait: {{ .Values.mqtt.ackWait | quote }} - max_ack_pending: {{ .Values.mqtt.maxAckPending }} - } - {{- end }} - - {{- if .Values.cluster.enabled }} - ################################### - # # - # NATS Full Mesh Clustering Setup # - # # - ################################### - cluster { - port: 6222 - - {{- if .Values.nats.jetstream.enabled }} - {{- if .Values.cluster.name }} - name: {{ .Values.cluster.name }} - {{- else }} - name: {{ template "nats.name" . }} - {{- end }} - {{- else }} - {{- with .Values.cluster.name }} - name: {{ . }} - {{- end }} - {{- end }} - - {{- with .Values.cluster.tls }} - {{- $cluster_tls := merge (dict) . }} - {{- $_ := set $cluster_tls "secretPath" "/etc/nats-certs/cluster" }} - {{- tpl (include "nats.tlsConfig" $cluster_tls) $ | nindent 6}} - {{- end }} - - {{- if .Values.cluster.authorization }} - authorization { - {{- with .Values.cluster.authorization.user }} - user: {{ . }} - {{- end }} - {{- with .Values.cluster.authorization.password }} - password: {{ . }} - {{- end }} - {{- with .Values.cluster.authorization.timeout }} - timeout: {{ . }} - {{- end }} - } - {{- end }} - - routes = [ - {{ include "nats.clusterRoutes" . }} - {{ include "nats.extraRoutes" . }} - ] - cluster_advertise: $CLUSTER_ADVERTISE - - {{- with .Values.cluster.noAdvertise }} - no_advertise: {{ . }} - {{- end }} - - connect_retries: {{ .Values.nats.connectRetries }} - } - {{- end }} - - {{- if and .Values.nats.advertise .Values.nats.externalAccess }} - include "advertise/client_advertise.conf" - {{- end }} - - {{- if or .Values.leafnodes.enabled .Values.leafnodes.remotes }} - ################# - # # - # NATS Leafnode # - # # - ################# - leafnodes { - {{- if .Values.leafnodes.enabled }} - listen: "0.0.0.0:7422" - {{- end }} - - {{- if and .Values.nats.advertise .Values.nats.externalAccess }} - include "advertise/gateway_advertise.conf" - {{- end }} - - {{- with .Values.leafnodes.noAdvertise }} - no_advertise: {{ . }} - {{- end }} - - {{- with .Values.leafnodes.authorization }} - authorization: { - {{- with .user }} - user: {{ . }} - {{- end }} - {{- with .password }} - password: {{ . }} - {{- end }} - {{- with .account }} - account: {{ . | quote }} - {{- end }} - {{- with .timeout }} - timeout: {{ . }} - {{- end }} - {{- with .users }} - users: [ - {{- range . }} - {{- toRawJson . | nindent 10 }}, - {{- end }} - ] - {{- end }} - } - {{- end }} - - {{- with .Values.leafnodes.tls }} - {{- if .custom }} - tls { - {{- .custom | nindent 8 }} - } - {{- else }} - {{- $leafnode_tls := merge (dict) . }} - {{- $_ := set $leafnode_tls "secretPath" "/etc/nats-certs/leafnodes" }} - {{- tpl (include "nats.tlsConfig" $leafnode_tls) $ | nindent 6}} - {{- end }} - {{- end }} - - remotes: [ - {{- range .Values.leafnodes.remotes }} - { - {{- with .url }} - url: {{ . | quote }} - {{- end }} - - {{- with .urls }} - urls: {{ toRawJson . }} - {{- end }} - - {{- with .account }} - account: {{ . | quote }} - {{- end }} - - {{- with .credentials }} - credentials: "/etc/nats-creds/{{ .secret.name }}/{{ .secret.key }}" - {{- end }} - - {{- with .tls }} - tls: { - {{- if .custom }} - {{- .custom | nindent 10 }} - {{- else }} - {{ $secretName := tpl .secret.name $ }} - {{- with .cert }} - cert_file: /etc/nats-certs/leafnodes/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .key }} - key_file: /etc/nats-certs/leafnodes/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .ca }} - ca_file: /etc/nats-certs/leafnodes/{{ $secretName }}/{{ . }} - {{- end }} - {{- end }} - } - {{- end }} - } - {{- end }} - ] - } - {{- end }} - - {{- if .Values.gateway.enabled }} - ################# - # # - # NATS Gateways # - # # - ################# - gateway { - name: {{ .Values.gateway.name }} - port: 7522 - - {{- if .Values.gateway.advertise }} - advertise: {{ .Values.gateway.advertise }} - {{- end }} - - {{- if .Values.gateway.rejectUnknownCluster }} - reject_unknown_cluster: {{ .Values.gateway.rejectUnknownCluster }} - {{- end }} - - {{- if .Values.gateway.authorization }} - authorization { - {{- with .Values.gateway.authorization.user }} - user: {{ . }} - {{- end }} - {{- with .Values.gateway.authorization.password }} - password: {{ . }} - {{- end }} - {{- with .Values.gateway.authorization.timeout }} - timeout: {{ . }} - {{- end }} - } - {{- end }} - - {{- if and .Values.nats.advertise .Values.nats.externalAccess }} - include "advertise/gateway_advertise.conf" - {{- end }} - - {{- with .Values.gateway.tls }} - {{- $gateway_tls := merge (dict) . }} - {{- $_ := set $gateway_tls "secretPath" "/etc/nats-certs/gateways" }} - {{- tpl (include "nats.tlsConfig" $gateway_tls) $ | nindent 6}} - {{- end }} - - # Gateways array here - gateways: [ - {{- range .Values.gateway.gateways }} - { - {{- with .name }} - name: {{ . }} - {{- end }} - - {{- with .url }} - url: {{ . | quote }} - {{- end }} - - {{- with .urls }} - urls: [{{ join "," . }}] - {{- end }} - }, - {{- end }} - ] - } - {{- end }} - - {{- with .Values.nats.logging.debug }} - debug: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.trace }} - trace: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.logtime }} - logtime: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.connectErrorReports }} - connect_error_reports: {{ . }} - {{- end }} - - {{- with .Values.nats.logging.reconnectErrorReports }} - reconnect_error_reports: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxConnections }} - max_connections: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxSubscriptions }} - max_subscriptions: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxPending }} - max_pending: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxControlLine }} - max_control_line: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxPayload }} - max_payload: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.pingInterval }} - ping_interval: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.maxPings }} - ping_max: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.writeDeadline }} - write_deadline: {{ . }} - {{- end }} - - {{- with .Values.nats.limits.lameDuckDuration }} - lame_duck_duration: {{ . }} - {{- end }} - - {{- if .Values.websocket.enabled }} - ################## - # # - # Websocket # - # # - ################## - websocket { - port: {{ .Values.websocket.port }} - {{- with .Values.websocket.tls }} - {{ $secretName := tpl .secret.name $ }} - tls { - {{- with .cert }} - cert_file: /etc/nats-certs/ws/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .key }} - key_file: /etc/nats-certs/ws/{{ $secretName }}/{{ . }} - {{- end }} - - {{- with .ca }} - ca_file: /etc/nats-certs/ws/{{ $secretName }}/{{ . }} - {{- end }} - } - {{- else }} - no_tls: {{ .Values.websocket.noTLS }} - {{- end }} - same_origin: {{ .Values.websocket.sameOrigin }} - {{- with .Values.websocket.allowedOrigins }} - allowed_origins: {{ toRawJson . }} - {{- end }} - } - {{- end }} - - {{- if .Values.auth.enabled }} - ################## - # # - # Authorization # - # # - ################## - {{- if .Values.auth.resolver }} - {{- if eq .Values.auth.resolver.type "memory" }} - resolver: MEMORY - include "accounts/{{ .Values.auth.resolver.configMap.key }}" - {{- end }} - - {{- if eq .Values.auth.resolver.type "full" }} - {{- if .Values.auth.resolver.configMap }} - include "accounts/{{ .Values.auth.resolver.configMap.key }}" - {{- else }} - {{- with .Values.auth.resolver }} - {{- if $.Values.auth.timeout }} - authorization { - timeout: {{ $.Values.auth.timeout }} - } - {{- end }} - - {{- if .operator }} - operator: {{ .operator }} - {{- end }} - - {{- if .systemAccount }} - system_account: {{ .systemAccount }} - {{- end }} - {{- end }} - - resolver: { - type: full - {{- with .Values.auth.resolver }} - dir: {{ .store.dir | quote }} - - allow_delete: {{ .allowDelete }} - - interval: {{ .interval | quote }} - {{- end }} - } - {{- end }} - {{- end }} - - {{- if .Values.auth.resolver.resolverPreload }} - resolver_preload: {{ toRawJson .Values.auth.resolver.resolverPreload }} - {{- end }} - - {{- if eq .Values.auth.resolver.type "URL" }} - {{- with .Values.auth.resolver.url }} - resolver: URL({{ . }}) - {{- end }} - operator: /etc/nats-config/operator/{{ .Values.auth.operatorjwt.configMap.key }} - {{- end }} - {{- end }} - - {{- with .Values.auth.systemAccount }} - system_account: {{ . }} - {{- end }} - - authorization { - {{- if .Values.auth.token }} - token: "{{ .Values.auth.token }}" - {{- else if .Values.auth.secret }} - token: $AUTH_TOKEN - {{- end }} - - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - } - {{- end }} - - {{- with .Values.auth.nkeys }} - {{- with .users }} - authorization { - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - - users: [ - {{- range . }} - {{- toRawJson . | nindent 4 }}, - {{- end }} - ] - } - {{- end }} - {{- end }} - - {{- with .Values.auth.basic }} - - {{- with .noAuthUser }} - no_auth_user: {{ . }} - {{- end }} - - {{- with .users }} - authorization { - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - - users: [ - {{- range . }} - {{- toRawJson . | nindent 4 }}, - {{- end }} - ] - } - {{- end }} - - {{- with .accounts }} - authorization { - {{- if $.Values.auth.timeout }} - timeout: {{ $.Values.auth.timeout }} - {{- end }} - } - - accounts: {{- toRawJson . }} - {{- end }} - - {{- end }} \ No newline at end of file diff --git a/charts/nats/templates/nats-box.yaml b/charts/nats/templates/nats-box.yaml deleted file mode 100644 index 0c9f3f67..00000000 --- a/charts/nats/templates/nats-box.yaml +++ /dev/null @@ -1,117 +0,0 @@ -{{- if .Values.natsbox.enabled }} ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "nats.fullname" . }}-box - namespace: {{ include "nats.namespace" . }} - labels: - app: {{ include "nats.fullname" . }}-box - chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} - {{- if .Values.natsbox.additionalLabels }} - {{- tpl (toYaml .Values.natsbox.additionalLabels) $ | nindent 4 }} - {{- end }} -spec: - replicas: 1 - selector: - matchLabels: - app: {{ include "nats.fullname" . }}-box - template: - metadata: - labels: - app: {{ include "nats.fullname" . }}-box - {{- if .Values.natsbox.podLabels }} - {{- tpl (toYaml .Values.natsbox.podLabels) $ | nindent 8 }} - {{- end }} - {{- if .Values.natsbox.podAnnotations }} - annotations: - {{- range $key, $value := .Values.natsbox.podAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} - spec: - {{- with .Values.natsbox.affinity }} - affinity: - {{- tpl (toYaml .) $ | nindent 8 }} - {{- end }} - {{- with .Values.natsbox.nodeSelector }} - nodeSelector: {{ toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.natsbox.tolerations }} - tolerations: {{ toYaml . | nindent 8 }} - {{- end }} - volumes: - {{- if .Values.natsbox.credentials }} - - name: nats-sys-creds - secret: - secretName: {{ .Values.natsbox.credentials.secret.name }} - {{- end }} - {{- if .Values.natsbox.extraVolumes }} - {{- toYaml .Values.natsbox.extraVolumes | nindent 6}} - {{- end }} - {{- with .Values.nats.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - secret: - secretName: {{ $secretName }} - {{- end }} -{{- with .Values.securityContext }} - securityContext: -{{ toYaml . | indent 8 }} -{{- end }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: nats-box - image: {{ .Values.natsbox.image }} - imagePullPolicy: {{ .Values.natsbox.pullPolicy }} - {{- if .Values.natsbox.securityContext }} - securityContext: - {{- .Values.natsbox.securityContext | toYaml | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.natsbox.resources | nindent 10 }} - env: - - name: NATS_URL - value: {{ template "nats.fullname" . }} - {{- if .Values.natsbox.credentials }} - - name: USER_CREDS - value: /etc/nats-config/creds/{{ .Values.natsbox.credentials.secret.key }} - - name: USER2_CREDS - value: /etc/nats-config/creds/{{ .Values.natsbox.credentials.secret.key }} - {{- end }} - {{- with .Values.nats.tls }} - {{ $secretName := tpl .secret.name $ }} - lifecycle: - postStart: - exec: - command: - - /bin/sh - - -c - - cp /etc/nats-certs/clients/{{ $secretName }}/* /usr/local/share/ca-certificates && update-ca-certificates - {{- end }} - command: - - "tail" - - "-f" - - "/dev/null" - volumeMounts: - {{- if .Values.natsbox.credentials }} - - name: nats-sys-creds - mountPath: /etc/nats-config/creds - {{- end }} - {{- if .Values.natsbox.extraVolumeMounts }} - {{- toYaml .Values.natsbox.extraVolumeMounts | nindent 8 }} - {{- end }} - {{- with .Values.nats.tls }} - ####################### - # # - # TLS Volumes Mounts # - # # - ####################### - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - mountPath: /etc/nats-certs/clients/{{ $secretName }} - {{- end }} -{{- end }} diff --git a/charts/nats/templates/networkpolicy.yaml b/charts/nats/templates/networkpolicy.yaml deleted file mode 100644 index c815af32..00000000 --- a/charts/nats/templates/networkpolicy.yaml +++ /dev/null @@ -1,80 +0,0 @@ -{{- if .Values.networkPolicy.enabled }} -kind: NetworkPolicy -apiVersion: {{ template "networkPolicy.apiVersion" . }} -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: {{- include "nats.selectorLabels" . | nindent 6 }} - policyTypes: - - Ingress - - Egress - egress: - # Allow dns resolution - - ports: - - port: 53 - protocol: UDP - # Allow outbound connections to other cluster pods - - ports: - - port: 4222 - protocol: TCP - - port: 6222 - protocol: TCP - - port: 8222 - protocol: TCP - - port: 7777 - protocol: TCP - - port: 7422 - protocol: TCP - - port: 7522 - protocol: TCP - to: - - podSelector: - matchLabels: {{- include "nats.selectorLabels" . | nindent 14 }} - {{- if .Values.networkPolicy.extraEgress }} - {{- include "tplvalues.render" ( dict "value" .Values.networkPolicy.extraEgress "context" $ ) | nindent 4 }} - {{- end }} - ingress: - # Allow inbound connections - - ports: - - port: 4222 - protocol: TCP - - port: 6222 - protocol: TCP - - port: 8222 - protocol: TCP - - port: 7777 - protocol: TCP - - port: 7422 - protocol: TCP - - port: 7522 - protocol: TCP - {{- if not .Values.networkPolicy.allowExternal }} - from: - - podSelector: - matchLabels: - {{ include "nats.fullname" . }}-client: "true" - - podSelector: - matchLabels: {{- include "nats.selectorLabels" . | nindent 14 }} - {{- if .Values.networkPolicy.ingressNSMatchLabels }} - - namespaceSelector: - matchLabels: - {{- range $key, $value := .Values.networkPolicy.ingressNSMatchLabels }} - {{ $key | quote }}: {{ $value | quote }} - {{- end }} - {{- if .Values.networkPolicy.ingressNSPodMatchLabels }} - podSelector: - matchLabels: - {{- range $key, $value := .Values.networkPolicy.ingressNSPodMatchLabels }} - {{ $key | quote }}: {{ $value | quote }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - {{- if .Values.networkPolicy.extraIngress }} - {{- include "tplvalues.render" ( dict "value" .Values.networkPolicy.extraIngress "context" $ ) | nindent 4 }} - {{- end }} -{{- end }} diff --git a/charts/nats/templates/pdb.yaml b/charts/nats/templates/pdb.yaml deleted file mode 100644 index b1140eb6..00000000 --- a/charts/nats/templates/pdb.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if .Values.podDisruptionBudget }} ---- -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} -spec: - {{- if .Values.podDisruptionBudget.minAvailable }} - minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} - {{- end }} - {{- if .Values.podDisruptionBudget.maxUnavailable }} - maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} - {{- end }} - selector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} -{{- end }} - diff --git a/charts/nats/templates/rbac.yaml b/charts/nats/templates/rbac.yaml deleted file mode 100644 index f1a1c4d7..00000000 --- a/charts/nats/templates/rbac.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{ if and .Values.nats.externalAccess .Values.nats.advertise }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ .Values.nats.serviceAccount }} - namespace: {{ include "nats.namespace" . }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ .Values.nats.serviceAccount }} -rules: -- apiGroups: [""] - resources: - - nodes - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ .Values.nats.serviceAccount }}-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ .Values.nats.serviceAccount }} -subjects: -- kind: ServiceAccount - name: {{ .Values.nats.serviceAccount }} - namespace: {{ include "nats.namespace" . }} -{{ end }} diff --git a/charts/nats/templates/service.yaml b/charts/nats/templates/service.yaml deleted file mode 100644 index acde1e64..00000000 --- a/charts/nats/templates/service.yaml +++ /dev/null @@ -1,74 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} - {{- if .Values.serviceAnnotations}} - annotations: - {{- range $key, $value := .Values.serviceAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} -spec: - selector: - {{- include "nats.selectorLabels" . | nindent 4 }} - clusterIP: None - {{- if .Values.topologyKeys }} - topologyKeys: - {{- .Values.topologyKeys | toYaml | nindent 4 }} - {{- end }} - ports: - {{- if .Values.websocket.enabled }} - - name: websocket - port: {{ .Values.websocket.port }} - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - {{- end }} - {{- if .Values.nats.profiling.enabled }} - - name: profiling - port: {{ .Values.nats.profiling.port }} - {{- if .Values.appProtocol.enabled }} - appProtocol: http - {{- end }} - {{- end }} - - name: {{ .Values.nats.client.portName }} - port: {{ .Values.nats.client.port }} - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - - name: cluster - port: 6222 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - - name: monitor - port: 8222 - {{- if .Values.appProtocol.enabled }} - appProtocol: http - {{- end }} - - name: metrics - port: 7777 - {{- if .Values.appProtocol.enabled }} - appProtocol: http - {{- end }} - - name: leafnodes - port: 7422 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - - name: gateways - port: 7522 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - {{- if .Values.mqtt.enabled }} - - name: mqtt - port: 1883 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - {{- end }} diff --git a/charts/nats/templates/serviceExternal.yaml b/charts/nats/templates/serviceExternal.yaml deleted file mode 100644 index 3c238ab7..00000000 --- a/charts/nats/templates/serviceExternal.yaml +++ /dev/null @@ -1,74 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "nats.fullname" . }}-external - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} - {{- if .Values.serviceAnnotations}} - annotations: - {{- range $key, $value := .Values.serviceAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} -spec: - type: {{ .Values.service.type }} - selector: - {{- include "nats.selectorLabels" . | nindent 4 }} - {{- if .Values.topologyKeys }} - topologyKeys: - {{- .Values.topologyKeys | toYaml | nindent 4 }} - {{- end }} - ports: - {{- if .Values.websocket.enabled }} - - name: websocket - port: {{ .Values.websocket.port }} - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - {{- end }} - {{- if .Values.nats.profiling.enabled }} - - name: profiling - port: {{ .Values.nats.profiling.port }} - {{- if .Values.appProtocol.enabled }} - appProtocol: http - {{- end }} - {{- end }} - - name: {{ .Values.nats.client.portName }} - port: {{ .Values.nats.client.port }} - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - - name: cluster - port: 6222 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - - name: monitor - port: 8222 - {{- if .Values.appProtocol.enabled }} - appProtocol: http - {{- end }} - - name: metrics - port: 7777 - {{- if .Values.appProtocol.enabled }} - appProtocol: http - {{- end }} - - name: leafnodes - port: 7422 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - - name: gateways - port: 7522 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - {{- if .Values.mqtt.enabled }} - - name: mqtt - port: 1883 - {{- if .Values.appProtocol.enabled }} - appProtocol: tcp - {{- end }} - {{- end }} diff --git a/charts/nats/templates/serviceMonitor.yaml b/charts/nats/templates/serviceMonitor.yaml deleted file mode 100644 index 374cbae0..00000000 --- a/charts/nats/templates/serviceMonitor.yaml +++ /dev/null @@ -1,40 +0,0 @@ -{{ if and .Values.exporter.enabled .Values.exporter.serviceMonitor.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ template "nats.fullname" . }} - {{- if .Values.exporter.serviceMonitor.namespace }} - namespace: {{ .Values.exporter.serviceMonitor.namespace }} - {{- else }} - namespace: {{ include "nats.namespace" . }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.labels }} - labels: - {{- range $key, $value := .Values.exporter.serviceMonitor.labels }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.annotations }} - annotations: - {{- range $key, $value := .Values.exporter.serviceMonitor.annotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} -spec: - endpoints: - - port: metrics - {{- if .Values.exporter.serviceMonitor.path }} - path: {{ .Values.exporter.serviceMonitor.path }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.interval }} - interval: {{ .Values.exporter.serviceMonitor.interval }} - {{- end }} - {{- if .Values.exporter.serviceMonitor.scrapeTimeout }} - scrapeTimeout: {{ .Values.exporter.serviceMonitor.scrapeTimeout }} - {{- end }} - namespaceSelector: - any: true - selector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} -{{- end }} diff --git a/charts/nats/templates/statefulset.yaml b/charts/nats/templates/statefulset.yaml deleted file mode 100644 index 2c90e60d..00000000 --- a/charts/nats/templates/statefulset.yaml +++ /dev/null @@ -1,642 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "nats.fullname" . }} - namespace: {{ include "nats.namespace" . }} - labels: - {{- include "nats.labels" . | nindent 4 }} - {{- if .Values.statefulSetAnnotations}} - annotations: - {{- range $key, $value := .Values.statefulSetAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} -spec: - selector: - matchLabels: - {{- include "nats.selectorLabels" . | nindent 6 }} - {{- if .Values.cluster.enabled }} - replicas: {{ .Values.cluster.replicas }} - {{- else }} - replicas: 1 - {{- end }} - serviceName: {{ include "nats.fullname" . }} - - podManagementPolicy: {{ .Values.podManagementPolicy }} - - template: - metadata: - {{- if or .Values.podAnnotations .Values.exporter.enabled }} - annotations: - {{- if .Values.exporter.enabled }} - prometheus.io/path: /metrics - prometheus.io/port: "7777" - prometheus.io/scrape: "true" - {{- end }} - {{- range $key, $value := .Values.podAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} - labels: - {{- include "nats.selectorLabels" . | nindent 8 }} - {{- if .Values.statefulSetPodLabels }} - {{- tpl (toYaml .Values.statefulSetPodLabels) . | nindent 8 }} - {{- end }} - spec: -{{- with .Values.imagePullSecrets }} - imagePullSecrets: -{{- toYaml . | nindent 8 }} -{{- end }} -{{- with .Values.securityContext }} - securityContext: -{{- toYaml . | nindent 8 }} -{{- end }} -{{- with .Values.affinity }} - affinity: -{{- tpl (toYaml .) $ | nindent 8 }} -{{- end }} -{{- with .Values.nodeSelector }} - nodeSelector: {{ toYaml . | nindent 8 }} -{{- end }} -{{- with .Values.tolerations }} - tolerations: {{ toYaml . | nindent 8 }} -{{- end }} -{{- if .Values.topologySpreadConstraints }} - topologySpreadConstraints: - {{- range .Values.topologySpreadConstraints }} - {{- if and .maxSkew .topologyKey }} - - maxSkew: {{ .maxSkew }} - topologyKey: {{ .topologyKey }} - {{- if .whenUnsatisfiable }} - whenUnsatisfiable: {{ .whenUnsatisfiable }} - {{- end }} - labelSelector: - matchLabels: - {{- include "nats.selectorLabels" $ | nindent 12 }} - {{- end }} - {{- end }} -{{- end }} -{{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName | quote }} -{{- end }} - # Common volumes for the containers. - volumes: - - name: config-volume - {{ if .Values.nats.customConfigSecret }} - secret: - secretName: {{ .Values.nats.customConfigSecret.name }} - {{ else }} - configMap: - name: {{ include "nats.fullname" . }}-config - {{ end }} - - {{/* User extended config volumes*/}} - {{- if .Values.nats.config }} - # User extended config volumes - {{- with .Values.nats.config }} - {{- . | toYaml | nindent 6 }} - {{- end }} - {{- end }} - - # Local volume shared with the reloader. - - name: pid - emptyDir: {} - - {{- if and .Values.auth.enabled .Values.auth.resolver }} - {{- if .Values.auth.resolver.configMap }} - - name: resolver-volume - configMap: - name: {{ .Values.auth.resolver.configMap.name }} - {{- end }} - - {{- if eq .Values.auth.resolver.type "URL" }} - - name: operator-jwt-volume - configMap: - name: {{ .Values.auth.operatorjwt.configMap.name }} - {{- end }} - {{- end }} - - {{- if and .Values.nats.externalAccess .Values.nats.advertise }} - # Local volume shared with the advertise config initializer. - - name: advertiseconfig - emptyDir: {} - {{- end }} - - {{- if and .Values.nats.jetstream.fileStorage.enabled .Values.nats.jetstream.fileStorage.existingClaim }} - # Persistent volume for jetstream running with file storage option - - name: {{ include "nats.fullname" . }}-js-pvc - persistentVolumeClaim: - claimName: {{ .Values.nats.jetstream.fileStorage.existingClaim | quote }} - {{- end }} - - ################# - # # - # TLS Volumes # - # # - ################# - {{- with .Values.nats.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.mqtt.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-mqtt-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.cluster.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-cluster-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.leafnodes.tls }} - {{- if not .custom }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-leafnodes-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- end }} - {{- with .Values.gateway.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-gateways-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- with .Values.websocket.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-ws-volume - secret: - secretName: {{ $secretName }} - {{- end }} - {{- if .Values.leafnodes.enabled }} - # - # Leafnode credential volumes - # - {{- range .Values.leafnodes.remotes }} - {{- with .credentials }} - - name: {{ .secret.name }}-volume - secret: - secretName: {{ .secret.name }} - {{- end }} - {{- with .tls }} - - name: {{ .secret.name }}-volume - secret: - secretName: {{ .secret.name }} - {{- end }} - {{- end }} - {{- end }} - - {{- if .Values.additionalVolumes }} - {{- toYaml .Values.additionalVolumes | nindent 6 }} - {{- end }} - - {{ if and .Values.nats.externalAccess .Values.nats.advertise }} - # Assume that we only use the service account in case we want to - # figure out what is the current external public IP from the server - # in order to be able to advertise correctly. - serviceAccountName: {{ .Values.nats.serviceAccount }} - {{ end }} - - # Required to be able to HUP signal and apply config - # reload to the server without restarting the pod. - shareProcessNamespace: true - - {{- if and .Values.nats.externalAccess .Values.nats.advertise }} - # Initializer container required to be able to lookup - # the external ip on which this node is running. - initContainers: - - name: bootconfig - command: - - nats-pod-bootconfig - - -f - - /etc/nats-config/advertise/client_advertise.conf - - -gf - - /etc/nats-config/advertise/gateway_advertise.conf - env: - - name: KUBERNETES_NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - image: {{ .Values.bootconfig.image }} - imagePullPolicy: {{ .Values.bootconfig.pullPolicy }} - {{- if .Values.bootconfig.securityContext }} - securityContext: - {{- .Values.bootconfig.securityContext | toYaml | nindent 8 }} - {{- end }} - resources: - {{- toYaml .Values.bootconfig.resources | nindent 10 }} - volumeMounts: - - mountPath: /etc/nats-config/advertise - name: advertiseconfig - subPath: advertise - {{- end }} - - ################# - # # - # NATS Server # - # # - ################# - terminationGracePeriodSeconds: {{ .Values.nats.terminationGracePeriodSeconds }} - containers: - - name: nats - image: {{ .Values.nats.image }} - imagePullPolicy: {{ .Values.nats.pullPolicy }} - {{- if .Values.nats.securityContext }} - securityContext: - {{- .Values.nats.securityContext | toYaml | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.nats.resources | nindent 10 }} - ports: - - containerPort: {{ .Values.nats.client.port }} - name: {{ .Values.nats.client.portName }} - {{- if .Values.nats.externalAccess }} - hostPort: {{ .Values.nats.client.port }} - {{- end }} - - containerPort: 7422 - name: leafnodes - {{- if .Values.nats.externalAccess }} - hostPort: 7422 - {{- end }} - - containerPort: 7522 - name: gateways - {{- if .Values.nats.externalAccess }} - hostPort: 7522 - {{- end }} - - containerPort: 6222 - name: cluster - - containerPort: 8222 - name: monitor - - containerPort: 7777 - name: metrics - {{- if .Values.mqtt.enabled }} - - containerPort: 1883 - name: mqtt - {{- if .Values.nats.externalAccess }} - hostPort: 1883 - {{- end }} - {{- end }} - {{- if .Values.websocket.enabled }} - - containerPort: {{ .Values.websocket.port }} - name: websocket - {{- if .Values.nats.externalAccess }} - hostPort: {{ .Values.websocket.port }} - {{- end }} - {{- end }} - {{- if .Values.nats.profiling.enabled }} - - containerPort: {{ .Values.nats.profiling.port }} - name: profiling - {{- end }} - - command: - - "nats-server" - - "--config" - - "/etc/nats-config/nats.conf" - {{- if .Values.nats.profiling.enabled }} - - "--profile={{ .Values.nats.profiling.port }}" - {{- end }} - - # Required to be able to define an environment variable - # that refers to other environment variables. This env var - # is later used as part of the configuration file. - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: SERVER_NAME - value: {{ .Values.nats.serverNamePrefix }}$(POD_NAME) - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: CLUSTER_ADVERTISE - value: {{ include "nats.clusterAdvertise" . }} - {{- if .Values.nats.jetstream.enabled }} - {{- with .Values.nats.jetstream.encryption }} - {{- with .secret }} - - name: JS_KEY - valueFrom: - secretKeyRef: - name: {{ .name }} - key: {{ .key }} - {{- end }} - {{- end }} - {{- end }} - {{- if and .Values.auth.enabled .Values.auth.secret }} - - name: AUTH_TOKEN - valueFrom: - secretKeyRef: - name: {{ .Values.auth.secret.name }} - key: {{ .Values.auth.secret.key }} - {{- end }} - volumeMounts: - - name: config-volume - mountPath: /etc/nats-config - - name: pid - mountPath: /var/run/nats - {{- if and .Values.nats.externalAccess .Values.nats.advertise }} - - mountPath: /etc/nats-config/advertise - name: advertiseconfig - subPath: advertise - {{- end }} - - {{/* User extended config volumes*/}} - {{- range .Values.nats.config }} - # User extended config volumes - - name: {{ .name }} - mountPath: /etc/nats-config/{{ .name }} - {{- end }} - - - {{- if and .Values.auth.enabled .Values.auth.resolver }} - {{- if eq .Values.auth.resolver.type "memory" }} - - name: resolver-volume - mountPath: /etc/nats-config/accounts - {{- end }} - - {{- if eq .Values.auth.resolver.type "full" }} - {{- if .Values.auth.resolver.configMap }} - - name: resolver-volume - mountPath: /etc/nats-config/accounts - {{- end }} - {{- if and .Values.auth.resolver .Values.auth.resolver.store }} - - name: nats-jwt-pvc - mountPath: {{ .Values.auth.resolver.store.dir }} - {{- end }} - {{- end }} - - {{- if eq .Values.auth.resolver.type "URL" }} - - name: operator-jwt-volume - mountPath: /etc/nats-config/operator - {{- end }} - {{- end }} - - {{- if .Values.nats.jetstream.fileStorage.enabled }} - - name: {{ include "nats.fullname" . }}-js-pvc - mountPath: {{ .Values.nats.jetstream.fileStorage.storageDirectory }} - {{- end }} - - {{- with .Values.nats.tls }} - ####################### - # # - # TLS Volumes Mounts # - # # - ####################### - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-clients-volume - mountPath: /etc/nats-certs/clients/{{ $secretName }} - {{- end }} - {{- with .Values.mqtt.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-mqtt-volume - mountPath: /etc/nats-certs/mqtt/{{ $secretName }} - {{- end }} - {{- with .Values.cluster.tls }} - {{- if not .custom }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-cluster-volume - mountPath: /etc/nats-certs/cluster/{{ $secretName }} - {{- end }} - {{- end }} - {{- with .Values.leafnodes.tls }} - {{- if not .custom }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-leafnodes-volume - mountPath: /etc/nats-certs/leafnodes/{{ $secretName }} - {{- end }} - {{- end }} - {{- with .Values.gateway.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-gateways-volume - mountPath: /etc/nats-certs/gateways/{{ $secretName }} - {{- end }} - - {{- with .Values.websocket.tls }} - {{ $secretName := tpl .secret.name $ }} - - name: {{ $secretName }}-ws-volume - mountPath: /etc/nats-certs/ws/{{ $secretName }} - {{- end }} - - {{- if .Values.leafnodes.enabled }} - # - # Leafnode credential volumes - # - {{- range .Values.leafnodes.remotes }} - {{- with .credentials }} - - name: {{ .secret.name }}-volume - mountPath: /etc/nats-creds/{{ .secret.name }} - {{- end }} - {{- with .tls }} - - name: {{ .secret.name }}-volume - mountPath: /etc/nats-certs/leafnodes/{{ .secret.name }} - {{- end }} - {{- end }} - {{- end }} - - {{- if .Values.additionalVolumeMounts }} - {{- toYaml .Values.additionalVolumeMounts | nindent 10 }} - {{- end }} - - ####################### - # # - # Healthcheck Probes # - # # - ####################### - {{- if .Values.nats.healthcheck }} - - {{- with .Values.nats.healthcheck.liveness }} - {{- if .enabled }} - livenessProbe: - httpGet: - path: / - port: 8222 - initialDelaySeconds: {{ .initialDelaySeconds }} - timeoutSeconds: {{ .timeoutSeconds }} - periodSeconds: {{ .periodSeconds }} - successThreshold: {{ .successThreshold }} - failureThreshold: {{ .failureThreshold }} - {{- if .terminationGracePeriodSeconds }} - terminationGracePeriodSeconds: {{ .terminationGracePeriodSeconds }} - {{- end }} - {{- end }} - {{- end }} - - {{- with .Values.nats.healthcheck.readiness }} - {{- if .enabled }} - readinessProbe: - httpGet: - path: / - port: 8222 - initialDelaySeconds: {{ .initialDelaySeconds }} - timeoutSeconds: {{ .timeoutSeconds }} - periodSeconds: {{ .periodSeconds }} - successThreshold: {{ .successThreshold }} - failureThreshold: {{ .failureThreshold }} - {{- end }} - {{- end }} - - {{- if .Values.nats.healthcheck.startup.enabled }} - startupProbe: - httpGet: - {{- $parts := split ":" .Values.nats.image }} - {{- $tag := $parts._1 }} - {{- $version := semver $tag }} - {{- $simpleVersion := printf "%d.%d.%d" $version.Major $version.Minor $version.Patch }} - {{- if and (and (or .Release.IsUpgrade .Values.upgrade) (semverCompare "~2.7.1" $simpleVersion) .Values.nats.healthcheck.enableHealthz ) }} - # During upgrades, healthz will be enabled instead to allow for a grace period - # in case of JetStream enabled deployments to form quorum and streams to catch up. - path: /healthz - {{- else }} - path: / - {{- end }} - port: 8222 - {{- with .Values.nats.healthcheck.startup }} - initialDelaySeconds: {{ .initialDelaySeconds }} - timeoutSeconds: {{ .timeoutSeconds }} - periodSeconds: {{ .periodSeconds }} - successThreshold: {{ .successThreshold }} - failureThreshold: {{ .failureThreshold }} - {{- end }} - {{- end }} - - {{- end }} - - # Gracefully stop NATS Server on pod deletion or image upgrade. - # - lifecycle: - preStop: - exec: - # Using the alpine based NATS image, we add an extra sleep that is - # the same amount as the terminationGracePeriodSeconds to allow - # the NATS Server to gracefully terminate the client connections. - # - command: - - "/bin/sh" - - "-c" - - "nats-server -sl=ldm=/var/run/nats/nats.pid" - - ################################# - # # - # NATS Configuration Reloader # - # # - ################################# - {{ if .Values.reloader.enabled }} - - name: reloader - image: {{ .Values.reloader.image }} - imagePullPolicy: {{ .Values.reloader.pullPolicy }} - {{- if .Values.reloader.securityContext }} - securityContext: - {{- .Values.reloader.securityContext | toYaml | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.reloader.resources | nindent 10 }} - command: - - "nats-server-config-reloader" - - "-pid" - - "/var/run/nats/nats.pid" - - "-config" - - "/etc/nats-config/nats.conf" - {{- range .Values.reloader.extraConfigs }} - - "-config" - - {{ . | quote }} - {{- end }} - volumeMounts: - - name: config-volume - mountPath: /etc/nats-config - - name: pid - mountPath: /var/run/nats - {{- if .Values.additionalVolumeMounts }} - {{- toYaml .Values.additionalVolumeMounts | nindent 10 }} - {{- end }} - {{ end }} - - ############################## - # # - # NATS Prometheus Exporter # - # # - ############################## - {{ if .Values.exporter.enabled }} - - name: metrics - image: {{ .Values.exporter.image }} - imagePullPolicy: {{ .Values.exporter.pullPolicy }} - {{- if .Values.exporter.securityContext }} - securityContext: - {{- .Values.exporter.securityContext | toYaml | nindent 10 }} - {{- end }} - resources: - {{- toYaml .Values.exporter.resources | nindent 10 }} - args: - - -connz - - -routez - - -subz - - -varz - - -prefix=nats - - -use_internal_server_id - {{- if .Values.nats.jetstream.enabled }} - - -jsz=all - {{- end }} - {{- if .Values.leafnodes.enabled }} - - -leafz - {{- end }} - - http://localhost:8222/ - ports: - - containerPort: 7777 - name: metrics - {{ end }} - - {{- if .Values.additionalContainers }} - {{- toYaml .Values.additionalContainers | nindent 6 }} - {{- end }} - - volumeClaimTemplates: - {{- if eq .Values.auth.resolver.type "full" }} - {{- if and .Values.auth.resolver .Values.auth.resolver.store }} - ##################################### - # # - # Account Server Embedded JWT # - # # - ##################################### - - metadata: - name: nats-jwt-pvc - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.auth.resolver.store.size }} - {{- end }} - {{- end }} - - {{- if and .Values.nats.jetstream.fileStorage.enabled (not .Values.nats.jetstream.fileStorage.existingClaim) }} - ##################################### - # # - # Jetstream New Persistent Volume # - # # - ##################################### - - metadata: - name: {{ include "nats.fullname" . }}-js-pvc - {{- if .Values.nats.jetstream.fileStorage.annotations }} - annotations: - {{- range $key, $value := .Values.nats.jetstream.fileStorage.annotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} - spec: - accessModes: - {{- range .Values.nats.jetstream.fileStorage.accessModes }} - - {{ . | quote }} - {{- end }} - resources: - requests: - storage: {{ .Values.nats.jetstream.fileStorage.size }} - {{- if .Values.nats.jetstream.fileStorage.storageClassName }} - storageClassName: {{ .Values.nats.jetstream.fileStorage.storageClassName | quote }} - {{- end }} - {{- end }} diff --git a/charts/nats/templates/tests/test-request-reply.yaml b/charts/nats/templates/tests/test-request-reply.yaml deleted file mode 100644 index 785ce53b..00000000 --- a/charts/nats/templates/tests/test-request-reply.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "nats.fullname" . }}-test-request-reply" - labels: - {{- include "nats.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: nats-box - image: synadia/nats-box - env: - - name: NATS_HOST - value: {{ template "nats.fullname" . }} - command: - - /bin/sh - - -ec - - | - nats reply -s nats://$NATS_HOST:4222 'name.>' --command "echo {{1}}" & - - | - "&&" - - | - name=$(nats request -s nats://$NATS_HOST:4222 name.test '' 2>/dev/null) - - | - "&&" - - | - [ $name = test ] - - restartPolicy: Never diff --git a/charts/nats/values.yaml b/charts/nats/values.yaml deleted file mode 100644 index e2839093..00000000 --- a/charts/nats/values.yaml +++ /dev/null @@ -1,682 +0,0 @@ -############################### -# # -# NATS Server Configuration # -# # -############################### -nats: - image: nats:2.7.2-alpine - pullPolicy: IfNotPresent - - # The servers name prefix, must be used for example when we want a NATS cluster - # spanning multiple Kubernetes clusters. - serverNamePrefix: "" - - # Toggle profiling. - # This enables nats-server pprof (profiling) port, so you can see goroutines - # stacks, memory heap sizes, etc. - profiling: - enabled: false - port: 6000 - - # Toggle using health check probes to better detect failures. - healthcheck: - # NOTE: Only works on NATS Server +2.7.1. - # This is recommended to be enabled for NATS JetStream deployments upgrades. - enableHealthz: true - - # Enable liveness checks. If this fails, then the NATS Server will restarted. - liveness: - enabled: true - - initialDelaySeconds: 10 - timeoutSeconds: 5 - # NOTE: liveness check + terminationGracePeriodSeconds can introduce unecessarily long outages - # due to the coupling between liveness probe and terminationGracePeriodSeconds. - # To avoid this, we make the periodSeconds of the liveness check to be about half the default - # time that it takes for lame duck graceful stop. - # - # In case of using Kubernetes +1.22 with probe-level terminationGracePeriodSeconds - # we could revise this but for now keep a minimal liveness check. - # - # More info: - # - # https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#probe-level-terminationgraceperiodseconds - # https://github.com/kubernetes/kubernetes/issues/64715 - # - periodSeconds: 60 - successThreshold: 1 - failureThreshold: 3 - # Only for Kubernetes +1.22 that have pod level probes enabled. - terminationGracePeriodSeconds: - - # Periodically check for the server to be ready for connections while - # the NATS container is running. - # Disabled by default since covered by startup probe and it is the same - # as the liveness check. - readiness: - enabled: false - - initialDelaySeconds: 10 - timeoutSeconds: 5 - periodSeconds: 10 - successThreshold: 1 - failureThreshold: 3 - - # Enable startup checks to confirm server is ready for traffic. - # This is recommended for JetStream deployments since in cluster mode - # it will try to ensure that the server is ready to serve streams. - startup: - enabled: true - - initialDelaySeconds: 10 - timeoutSeconds: 5 - periodSeconds: 10 - successThreshold: 1 - failureThreshold: 30 - - # securityContext for the nats container - securityContext: {} - - # Toggle whether to enable external access. - # This binds a host port for clients, gateways and leafnodes. - externalAccess: false - - # Toggle to disable client advertisements (connect_urls), - # in case of running behind a load balancer (which is not recommended) - # it might be required to disable advertisements. - advertise: true - - # In case both external access and advertise are enabled - # then a service account would be required to be able to - # gather the public ip from a node. - serviceAccount: "nats-server" - - # The number of connect attempts against discovered routes. - connectRetries: 120 - - # selector matchLabels for the server and service. - # If left empty defaults are used. - # This is helpful if you are updating from Chart version <=7.4 - selectorLabels: {} - - resources: {} - - client: - port: 4222 - portName: "client" - - # Server settings. - limits: - maxConnections: - maxSubscriptions: - maxControlLine: - maxPayload: "64MB" - - writeDeadline: - maxPending: - maxPings: - - # How many seconds should pass before sending a PING - # to a client that has no activity. - pingInterval: - - # NOTE: this should be at least the same as 'terminationGracePeriodSeconds' - lameDuckDuration: "120s" - - # terminationGracePeriodSeconds determines how long to allow for a pod - # to be restarted. - terminationGracePeriodSeconds: 120 - - logging: - debug: - trace: - logtime: - connectErrorReports: - reconnectErrorReports: - - # customConfigSecret can be used to use an custom secret for the config - # of the NATS Server. - # NOTE: For this to work the name of the configuration has to be - # called `nats.conf`. - # - # e.g. kubectl create secret generic custom-nats-conf --from-file nats.conf - # - # customConfigSecret: - # name: - # - # Alternately, the generated config can be extended with extra imports using the below syntax. - # The benefit of this is that cluster settings can be built up via helm values, but external - # secrets can be referenced and imported alongside it. - # - # config: - # : - # - # name: "" - # - # e.g: - # - # config: - # - name: ssh-key - # secret: - # secretName: ssh-key - # - name: config-vol - # configMap: - # name: log-config - - jetstream: - enabled: false - - # Jetstream Domain - domain: - - ########################## - # # - # Jetstream Encryption # - # # - ########################## - encryption: - # Use key if you want to provide the key via Helm Values - # key: random_key - - # Use a secret reference if you want to get a key from a secret - # secret: - # name: "nats-jetstream-encryption" - # key: "key" - - ############################# - # # - # Jetstream Memory Storage # - # # - ############################# - memStorage: - enabled: true - size: 1Gi - - ############################ - # # - # Jetstream File Storage # - # # - ############################ - fileStorage: - enabled: false - storageDirectory: /data - - # Set for use with existing PVC - # existingClaim: jetstream-pvc - # claimStorageSize: 1Gi - - # Use below block to create new persistent volume - # only used if existingClaim is not specified - size: 1Gi - # storageClassName: "" - accessModes: - - ReadWriteOnce - annotations: - # key: "value" - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - - # tls: - # allow_non_tls: false - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -mqtt: - enabled: false - ackWait: 1m - maxAckPending: 100 - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - - # - # tls: - # secret: - # name: nats-mqtt-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -nameOverride: "" -namespaceOverride: "" - -# An array of imagePullSecrets, and they have to be created manually in the same namespace -# ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ -imagePullSecrets: [] - -# Toggle whether to use setup a Pod Security Context -# ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ -securityContext: {} -# securityContext: -# fsGroup: 1000 -# runAsUser: 1000 -# runAsNonRoot: true - -# Affinity for pod assignment -# ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity -affinity: {} - -## Pod priority class name -## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass -priorityClassName: null - -# Service topology -# ref: https://kubernetes.io/docs/concepts/services-networking/service-topology/ -topologyKeys: [] - -# Pod Topology Spread Constraints -# ref https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ -topologySpreadConstraints: [] -# - maxSkew: 1 -# topologyKey: zone -# whenUnsatisfiable: DoNotSchedule - -# Annotations to add to the NATS pods -# ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ -podAnnotations: {} -# key: "value" - -# Define a Pod Disruption Budget for the stateful set -# ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ -podDisruptionBudget: - # minAvailable: 1 - # maxUnavailable: 1 - -# Node labels for pod assignment -# Ref: https://kubernetes.io/docs/user-guide/node-selection/ -nodeSelector: {} - -# Node tolerations for server scheduling to nodes with taints -# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ -# -tolerations: [] -# - key: "key" -# operator: "Equal|Exists" -# value: "value" -# effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" - -# Annotations to add to the NATS StatefulSet -statefulSetAnnotations: {} - -# Labels to add to the pods of the NATS StatefulSet -statefulSetPodLabels: {} - -# Annotations to add to the NATS Service -serviceAnnotations: {} - -# additionalContainers are the sidecar containers to add to the NATS StatefulSet -additionalContainers: [] - -# additionalVolumes are the additional volumes to add to the NATS StatefulSet -additionalVolumes: [] - -# additionalVolumeMounts are the additional volume mounts to add to the nats-server and nats-server-config-reloader containers -additionalVolumeMounts: [] - -cluster: - enabled: false - replicas: 3 - noAdvertise: false - - # Explicitly set routes for clustering. - # When JetStream is enabled, the serverName must be unique in the cluster. - extraRoutes: [] - - # authorization: - # user: foo - # password: pwd - # timeout: 0.5 - -# Leafnode connections to extend a cluster: -# -# https://docs.nats.io/nats-server/configuration/leafnodes -# -leafnodes: - enabled: false - noAdvertise: false - # remotes: - # - url: "tls://connect.ngs.global:7422" - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - - # - # tls: - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -# Gateway connections to create a super cluster -# -# https://docs.nats.io/nats-server/configuration/gateways -# -gateway: - enabled: false - name: "default" - # authorization: - # user: foo - # password: pwd - # timeout: 0.5 - # rejectUnknownCluster: false - - # You can add an implicit advertise address instead of using from Node's IP - # could also be a fqdn address - # advertise: "nats.example.com" - - ############################# - # # - # List of remote gateways # - # # - ############################# - # gateways: - # - name: other - # url: nats://my-gateway-url:7522 - - ####################### - # # - # TLS Configuration # - # # - ####################### - # - # # You can find more on how to setup and trouble shoot TLS connnections at: - # - # # https://docs.nats.io/nats-server/configuration/securing_nats/tls - # - # tls: - # secret: - # name: nats-client-tls - # ca: "ca.crt" - # cert: "tls.crt" - # key: "tls.key" - -# In case of both external access and advertisements being -# enabled, an initializer container will be used to gather -# the public ips. -bootconfig: - image: natsio/nats-boot-config:0.5.4 - pullPolicy: IfNotPresent - securityContext: {} - -# NATS Box -# -# https://github.com/nats-io/nats-box -# -natsbox: - enabled: true - image: natsio/nats-box:0.8.1 - pullPolicy: IfNotPresent - securityContext: {} - - # Labels to add to the natsbox deployment - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - additionalLabels: {} - - # An array of imagePullSecrets, and they have to be created manually in the same namespace - # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ - imagePullSecrets: [] - # - name: dockerhub - - # credentials: - # secret: - # name: nats-sys-creds - # key: sys.creds - - # Annotations to add to the box pods - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - podAnnotations: {} - # key: "value" - - # Labels to add to the box pods - # ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - podLabels: {} - # key: "value" - - # Affinity for nats box pod assignment - # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - affinity: {} - - # Node labels for pod assignment - # Ref: https://kubernetes.io/docs/user-guide/node-selection/ - nodeSelector: {} - - # Node tolerations for server scheduling to nodes with taints - # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - # - tolerations: [] - # - key: "key" - # operator: "Equal|Exists" - # value: "value" - # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" - - # Additional nats-box server Volume mounts - extraVolumeMounts: [] - - # Additional nats-box server Volumes - extraVolumes: [] - -# The NATS config reloader image to use. -reloader: - enabled: true - image: natsio/nats-server-config-reloader:0.6.2 - pullPolicy: IfNotPresent - securityContext: {} - extraConfigs: [] - -# Prometheus NATS Exporter configuration. -exporter: - enabled: true - image: natsio/prometheus-nats-exporter:0.9.1 - pullPolicy: IfNotPresent - securityContext: {} - resources: {} - # Prometheus operator ServiceMonitor support. Exporter has to be enabled - serviceMonitor: - enabled: false - ## Specify the namespace where Prometheus Operator is running - ## - # namespace: monitoring - labels: {} - annotations: {} - path: /metrics - # interval: - # scrapeTimeout: - -# Authentication setup -auth: - enabled: false - - # basic: - # noAuthUser: - # # List of users that can connect with basic auth, - # # that belong to the global account. - # users: - - # # List of accounts with users that can connect - # # using basic auth. - # accounts: - - # Reference to the Operator JWT. - # operatorjwt: - # configMap: - # name: operator-jwt - # key: KO.jwt - - # Use key if you want to provide the token via Helm Values - # token: - - # Use a secret reference if you want to get a token from a secret - # secret: - # name: "nats-token" - # key: "key" - - # NKey authentication - # nkeys: - # users: - - # Public key of the System Account - # systemAccount: - - resolver: - # Disables the resolver by default - type: none - - ########################################## - # # - # Embedded NATS Account Server Resolver # - # # - ########################################## - # type: full - - # If the resolver type is 'full', delete when enabled will rename the jwt. - allowDelete: false - - # Interval at which a nats-server with a nats based account resolver will compare - # it's state with one random nats based account resolver in the cluster and if needed, - # exchange jwt and converge on the same set of jwt. - interval: 2m - - # Operator JWT - operator: - - # System Account Public NKEY - systemAccount: - - # resolverPreload: - # : - - # Directory in which the account JWTs will be stored. - store: - dir: "/accounts/jwt" - - # Size of the account JWT storage. - size: 1Gi - - ############################## - # # - # Memory resolver settings # - # # - ############################## - # type: memory - # - # Use a configmap reference which will be mounted - # into the container. - # - # configMap: - # name: nats-accounts - # key: resolver.conf - - ########################## - # # - # URL resolver settings # - # # - ########################## - # type: URL - # url: "http://nats-account-server:9090/jwt/v1/accounts/" - -websocket: - enabled: false - port: 443 - noTLS: true - - sameOrigin: false - allowedOrigins: [] - -appProtocol: - enabled: false - -# Network Policy configuration -networkPolicy: - enabled: false - # Don't require client label for connections - # When set to false, only pods with the correct client label will have network access to the ports - # NATS is listening on. When true, NATS will accept connections from any source - # (with the correct destination port). - allowExternal: true - # Add extra ingress rules to the NetworkPolicy - # e.g: - # extraIngress: - # - ports: - # - port: 1234 - # from: - # - podSelector: - # - matchLabels: - # - role: frontend - # - podSelector: - # - matchExpressions: - # - key: role - # operator: In - # values: - # - frontend - extraIngress: [] - # Add extra ingress rules to the NetworkPolicy - # e.g: - # extraEgress: - # - ports: - # - port: 1234 - # to: - # - podSelector: - # - matchLabels: - # - role: frontend - # - podSelector: - # - matchExpressions: - # - key: role - # operator: In - # values: - # - frontend - extraEgress: [] - # Labels to match to allow traffic from other namespaces - ingressNSMatchLabels: {} - # Pod labels to match to allow traffic from other namespaces - ingressNSPodMatchLabels: {} - -# Cluster Domain configured on the kubelets -# https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ -k8sClusterDomain: cluster.local - -# Define if NATS is using FQDN name for clustering (i.e. nats-0.nats.default.svc.cluster.local) or short name (i.e. nats-0.nats.default). -useFQDN: true - -# Add labels to all the deployed resources -commonLabels: {} - -# podManagementPolicy controls how pods are created during initial scale up, -# when replacing pods on nodes, or when scaling down. -podManagementPolicy: Parallel - -# Toggle that this is an upgrade to enable healthz, in case not -# using `helm upgrade` command to apply upgrades. -upgrade: false - -service: - type: LoadBalancer - From 9ceb61d571365af9e4a8c3d34253a588116934c6 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 18 Dec 2023 14:16:16 +0530 Subject: [PATCH 147/263] updated clickhouse dependency version --- charts/client/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index ff52050e..fb8dc1ef 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -29,7 +29,7 @@ dependencies: repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: clickhouse condition: clickhouse.enabled - version: 1.0.0 + version: 1.0.1 repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: grafana condition: grafana.enabled From e3fcc280e3005a6da137ad3c1a044a2bfd088ed4 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 20:30:32 +0530 Subject: [PATCH 148/263] kubescore added --- agent/kubviz/k8smetrics_agent.go | 4 +-- agent/kubviz/kube_score.go | 57 ++++++++++++++---------------- agent/kubviz/scheduler_watch.go | 9 +++-- client/pkg/clickhouse/db_client.go | 43 +++++++++++++++++----- go.mod | 3 +- go.sum | 6 ++-- model/kubescore.go | 9 ++--- 7 files changed, 78 insertions(+), 53 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 49230271..bcd6628f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -130,7 +130,7 @@ func main() { //getK8sEvents(clientset) err = runTrivyScans(config, js) LogErr(err) - err = RunKubeScore(clientset, js) + err = RunKubeScore(config, js) LogErr(err) } @@ -321,7 +321,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age } } if cfg.KubeScoreInterval != "" && cfg.KubeScoreInterval != "0" { - sj, err := NewKubescoreJob(clientset, js, cfg.KubeScoreInterval) + sj, err := NewKubescoreJob(config, js, cfg.KubeScoreInterval) if err != nil { log.Fatal("no time interval", err) } diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index 437a0850..b0a3a405 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -1,7 +1,6 @@ package main import ( - "context" "encoding/json" "log" exec "os/exec" @@ -10,57 +9,53 @@ import ( "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/zegl/kube-score/renderer/json_v2" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" ) -func RunKubeScore(clientset *kubernetes.Clientset, js nats.JetStreamContext) error { - nsList, err := clientset.CoreV1(). - Namespaces(). - List(context.Background(), metav1.ListOptions{}) +func RunKubeScore(config *rest.Config, js nats.JetStreamContext) error { + _, err := kubernetes.NewForConfig(config) if err != nil { - log.Println("Error occurred while getting client set for kube-score: ", err) + log.Printf("Error creating Kubernetes clientset: %v", err) return err } - - log.Printf("Namespace size: %d", len(nsList.Items)) - for _, n := range nsList.Items { - log.Printf("Publishing kube-score recommendations for namespace: %s\n", n.Name) - publish(n.Name, js) - } - return nil -} - -func publish(ns string, js nats.JetStreamContext) error { - cmd := "kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c \"kubectl get {} -n " + ns + " -oyaml && echo ---\" | kube-score score - " + //defer wg.Done() + var report []json_v2.ScoredObject + cmd := `kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c "kubectl get {} --all-namespaces -oyaml && echo ---" | kube-score score - -o json` log.Printf("Command: %#v,", cmd) out, err := executeCommand(cmd) if err != nil { - log.Println("Error occurred while running kube-score: ", err) + log.Printf("Error scanning image %s:", err) return err } - err = publishKubescoreMetrics(uuid.New().String(), ns, out, js) + + err = json.Unmarshal([]byte(out), &report) if err != nil { + log.Printf("Error occurred while Unmarshalling json: %v", err) return err } + + publishKubescoreMetrics(report, js) return nil } -func publishKubescoreMetrics(id string, ns string, recommendations string, js nats.JetStreamContext) error { +func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamContext) { metrics := model.KubeScoreRecommendations{ - ID: id, - Namespace: ns, - Recommendations: recommendations, - ClusterName: ClusterName, + ID: uuid.New().String(), + ClusterName: ClusterName, + Report: report, } - metricsJson, _ := json.Marshal(metrics) - _, err := js.Publish(constants.KUBESCORE_SUBJECT, metricsJson) + metricsJson, err := json.Marshal(metrics) if err != nil { - return err + log.Printf("Error marshaling metrics to JSON: %v", err) + return + } + _, err = js.Publish(constants.KUBESCORE_SUBJECT, metricsJson) + if err != nil { + log.Printf("error occures while publish %v", err) + return } - log.Printf("Recommendations with ID:%s has been published\n", id) - log.Printf("Recommendations :%#v", recommendations) - return nil } func executeCommand(command string) (string, error) { diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler_watch.go index 6683816d..8a2d408b 100644 --- a/agent/kubviz/scheduler_watch.go +++ b/agent/kubviz/scheduler_watch.go @@ -2,7 +2,6 @@ package main import ( "github.com/nats-io/nats.go" - "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) @@ -33,7 +32,7 @@ type KubePreUpgradeJob struct { frequency string } type KubescoreJob struct { - clientset *kubernetes.Clientset + config *rest.Config js nats.JetStreamContext frequency string } @@ -88,9 +87,9 @@ func (j *KubePreUpgradeJob) Run() { LogErr(err) } -func NewKubescoreJob(clientset *kubernetes.Clientset, js nats.JetStreamContext, frequency string) (*KubescoreJob, error) { +func NewKubescoreJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*KubescoreJob, error) { return &KubescoreJob{ - clientset: clientset, + config: config, js: js, frequency: frequency, }, nil @@ -101,7 +100,7 @@ func (v *KubescoreJob) CronSpec() string { func (j *KubescoreJob) Run() { // Call the Kubescore function with the provided config and js - err := RunKubeScore(j.clientset, j.js) + err := RunKubeScore(j.config, j.js) LogErr(err) } func NewRakkessJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*RakkessJob, error) { diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 82fd4359..0f10badc 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -523,6 +523,7 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) } + defer tx.Rollback() stmt, err := tx.Prepare(InsertKubeScore) if err != nil { log.Fatalf("error preparing statement: %v", err) @@ -531,14 +532,40 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations currentTime := time.Now().UTC() - if _, err := stmt.Exec( - metrics.ID, - metrics.Namespace, - metrics.ClusterName, - metrics.Recommendations, - currentTime, - ); err != nil { - log.Fatal(err) + // if _, err := stmt.Exec( + // metrics.ID, + // metrics.Namespace, + // metrics.ClusterName, + // metrics.Recommendations, + // currentTime, + // ); err != nil { + // log.Fatal(err) + // } + for _, result := range metrics.Report { + for _, check := range result.Checks { + for _, comments := range check.Comments { + + if _, err := stmt.Exec( + metrics.ID, + metrics.ClusterName, + result.ObjectName, + result.TypeMeta.Kind, + result.TypeMeta.APIVersion, + result.ObjectMeta.Name, + result.ObjectMeta.Namespace, + check.Check.TargetType, + comments.Description, + comments.Path, + comments.Summary, + result.FileName, + result.FileRow, + currentTime, + ); err != nil { + log.Println("Error while inserting KubeScore metrics:", err) + } + } + + } } if err := tx.Commit(); err != nil { log.Fatal(err) diff --git a/go.mod b/go.mod index df47ddff..48f274bd 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 + github.com/zegl/kube-score v1.17.0 golang.org/x/term v0.11.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 @@ -53,7 +54,7 @@ require ( github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fatih/color v1.14.1 // indirect + github.com/fatih/color v1.15.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect diff --git a/go.sum b/go.sum index b8b6d6ff..ce5d958d 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= @@ -472,6 +472,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0= +github.com/zegl/kube-score v1.17.0 h1:vedzK0pm5yOb1ocm5gybMNYsJRG8iTAatbo3LFIWbUc= +github.com/zegl/kube-score v1.17.0/go.mod h1:0pt4Lt36uTKPiCQbXQFow29eaAbgMLI9RoESjBoGSq0= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= diff --git a/model/kubescore.go b/model/kubescore.go index 918ada85..fb63f747 100644 --- a/model/kubescore.go +++ b/model/kubescore.go @@ -1,8 +1,9 @@ package model +import "github.com/zegl/kube-score/renderer/json_v2" + type KubeScoreRecommendations struct { - ID string - Namespace string - Recommendations string - ClusterName string + ID string + ClusterName string + Report []json_v2.ScoredObject } From 4289484981aa4776cd4ddd9cf73a290716c5866f Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 20:31:12 +0530 Subject: [PATCH 149/263] kubescore added --- agent/kubviz/k8smetrics_agent.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index bcd6628f..84e28c9c 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -119,17 +119,17 @@ func main() { go publishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { - err := outDatedImages(config, js) - LogErr(err) - err = KubePreUpgradeDetector(config, js) - LogErr(err) - err = GetAllResources(config, js) - LogErr(err) - err = RakeesOutput(config, js) - LogErr(err) - //getK8sEvents(clientset) - err = runTrivyScans(config, js) - LogErr(err) + // err := outDatedImages(config, js) + // LogErr(err) + // err = KubePreUpgradeDetector(config, js) + // LogErr(err) + // err = GetAllResources(config, js) + // LogErr(err) + // err = RakeesOutput(config, js) + // LogErr(err) + // //getK8sEvents(clientset) + // err = runTrivyScans(config, js) + // LogErr(err) err = RunKubeScore(config, js) LogErr(err) } From 3244b1f5c596355508400ee83d02495815dea815 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 20:57:19 +0530 Subject: [PATCH 150/263] kubescore added --- agent/kubviz/kube_score.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index b0a3a405..52de7d32 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -10,16 +10,15 @@ import ( "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" "github.com/zegl/kube-score/renderer/json_v2" - "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) func RunKubeScore(config *rest.Config, js nats.JetStreamContext) error { - _, err := kubernetes.NewForConfig(config) - if err != nil { - log.Printf("Error creating Kubernetes clientset: %v", err) - return err - } + // _, err := kubernetes.NewForConfig(config) + // if err != nil { + // log.Printf("Error creating Kubernetes clientset: %v", err) + // return err + // } //defer wg.Done() var report []json_v2.ScoredObject cmd := `kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c "kubectl get {} --all-namespaces -oyaml && echo ---" | kube-score score - -o json` From b5e4d9f09000d12f974d958fdd0b21f63e79cc26 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 21:19:38 +0530 Subject: [PATCH 151/263] kubescore added --- agent/kubviz/kube_score.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index 52de7d32..093ad319 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "log" exec "os/exec" @@ -21,8 +22,8 @@ func RunKubeScore(config *rest.Config, js nats.JetStreamContext) error { // } //defer wg.Done() var report []json_v2.ScoredObject - cmd := `kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c "kubectl get {} --all-namespaces -oyaml && echo ---" | kube-score score - -o json` - log.Printf("Command: %#v,", cmd) + cmd := fmt.Sprintf(`kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c "kubectl get {} --all-namespaces -oyaml && echo ---" | kube-score score - -o json`) + //log.Printf("Command: %#v,", cmd) out, err := executeCommand(cmd) if err != nil { log.Printf("Error scanning image %s:", err) From 660431858ba28ee145155f18fed0bc5054e80bfc Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 21:29:04 +0530 Subject: [PATCH 152/263] kubescore added --- agent/kubviz/kube_score.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index 093ad319..8f38e47b 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -23,13 +23,19 @@ func RunKubeScore(config *rest.Config, js nats.JetStreamContext) error { //defer wg.Done() var report []json_v2.ScoredObject cmd := fmt.Sprintf(`kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c "kubectl get {} --all-namespaces -oyaml && echo ---" | kube-score score - -o json`) - //log.Printf("Command: %#v,", cmd) + log.Printf("Command: %s", cmd) + + // Execute the command out, err := executeCommand(cmd) if err != nil { - log.Printf("Error scanning image %s:", err) + log.Printf("Error executing command: %s", err) return err } + // Log the output of the kubectl command + log.Printf("kubectl Command Output: %s", out) + + // Continue with the rest of the code... err = json.Unmarshal([]byte(out), &report) if err != nil { log.Printf("Error occurred while Unmarshalling json: %v", err) From cc14aa7e9a4f0b3a70f763b86f0287eb667c1df5 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 21:41:42 +0530 Subject: [PATCH 153/263] kubescore added --- agent/kubviz/kube_score.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index 8f38e47b..f801c597 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -69,10 +69,12 @@ func executeCommand(command string) (string, error) { stdout, err := cmd.Output() if err != nil { - log.Println("Execute Command Error", err.Error()) + log.Printf("Error executing command: %s", err) + return "", err } // Print the output - log.Println(string(stdout)) + log.Printf("Command Output: %s", string(stdout)) + return string(stdout), nil } From 23205ab071cf3563e5c9b426a8e8884390a0568d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 23:11:43 +0530 Subject: [PATCH 154/263] kubescore added --- agent/kubviz/k8smetrics_agent.go | 4 +- agent/kubviz/kube_score.go | 70 ++++++++++++++++------------- agent/kubviz/scheduler_watch.go | 9 ++-- client/pkg/clickhouse/db_client.go | 9 ---- client/pkg/clickhouse/statements.go | 2 +- sql/000008_kubescore.up.sql | 17 +++++-- 6 files changed, 59 insertions(+), 52 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 84e28c9c..64595314 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -130,7 +130,7 @@ func main() { // //getK8sEvents(clientset) // err = runTrivyScans(config, js) // LogErr(err) - err = RunKubeScore(config, js) + err = RunKubeScore(clientset, js) LogErr(err) } @@ -321,7 +321,7 @@ func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age } } if cfg.KubeScoreInterval != "" && cfg.KubeScoreInterval != "0" { - sj, err := NewKubescoreJob(config, js, cfg.KubeScoreInterval) + sj, err := NewKubescoreJob(clientset, js, cfg.KubeScoreInterval) if err != nil { log.Fatal("no time interval", err) } diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index f801c597..57ea8cb8 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -1,8 +1,8 @@ package main import ( + "context" "encoding/json" - "fmt" "log" exec "os/exec" @@ -11,31 +11,37 @@ import ( "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" "github.com/zegl/kube-score/renderer/json_v2" - "k8s.io/client-go/rest" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" ) -func RunKubeScore(config *rest.Config, js nats.JetStreamContext) error { - // _, err := kubernetes.NewForConfig(config) - // if err != nil { - // log.Printf("Error creating Kubernetes clientset: %v", err) - // return err - // } - //defer wg.Done() - var report []json_v2.ScoredObject - cmd := fmt.Sprintf(`kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c "kubectl get {} --all-namespaces -oyaml && echo ---" | kube-score score - -o json`) - log.Printf("Command: %s", cmd) - - // Execute the command - out, err := executeCommand(cmd) +func RunKubeScore(clientset *kubernetes.Clientset, js nats.JetStreamContext) error { + nsList, err := clientset.CoreV1(). + Namespaces(). + List(context.Background(), metav1.ListOptions{}) if err != nil { - log.Printf("Error executing command: %s", err) + log.Println("Error occurred while getting client set for kube-score: ", err) return err } - // Log the output of the kubectl command - log.Printf("kubectl Command Output: %s", out) + log.Printf("Namespace size: %d", len(nsList.Items)) + for _, n := range nsList.Items { + log.Printf("Publishing kube-score recommendations for namespace: %s\n", n.Name) + publish(n.Name, js) + } + return nil +} - // Continue with the rest of the code... +func publish(ns string, js nats.JetStreamContext) error { + var report []json_v2.ScoredObject + cmd := "kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c \"kubectl get {} -n " + ns + " -oyaml && echo ---\" | kube-score score - -o json" + log.Printf("Command: %#v,", cmd) + out, err := executeCommand(cmd) + if err != nil { + log.Println("Error occurred while running kube-score: ", err) + return err + } + // // Continue with the rest of the code... err = json.Unmarshal([]byte(out), &report) if err != nil { log.Printf("Error occurred while Unmarshalling json: %v", err) @@ -43,25 +49,27 @@ func RunKubeScore(config *rest.Config, js nats.JetStreamContext) error { } publishKubescoreMetrics(report, js) + //err = publishKubescoreMetrics(uuid.New().String(), ns, out, js) + if err != nil { + return err + } return nil } -func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamContext) { +func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamContext) error { metrics := model.KubeScoreRecommendations{ ID: uuid.New().String(), ClusterName: ClusterName, Report: report, } - metricsJson, err := json.Marshal(metrics) + metricsJson, _ := json.Marshal(metrics) + _, err := js.Publish(constants.KUBESCORE_SUBJECT, metricsJson) if err != nil { - log.Printf("Error marshaling metrics to JSON: %v", err) - return - } - _, err = js.Publish(constants.KUBESCORE_SUBJECT, metricsJson) - if err != nil { - log.Printf("error occures while publish %v", err) - return + return err } + //log.Printf("Recommendations with ID:%s has been published\n", id) + log.Printf("Recommendations :%#v", report) + return nil } func executeCommand(command string) (string, error) { @@ -69,12 +77,10 @@ func executeCommand(command string) (string, error) { stdout, err := cmd.Output() if err != nil { - log.Printf("Error executing command: %s", err) - return "", err + log.Println("Execute Command Error", err.Error()) } // Print the output - log.Printf("Command Output: %s", string(stdout)) - + log.Println(string(stdout)) return string(stdout), nil } diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler_watch.go index 8a2d408b..6683816d 100644 --- a/agent/kubviz/scheduler_watch.go +++ b/agent/kubviz/scheduler_watch.go @@ -2,6 +2,7 @@ package main import ( "github.com/nats-io/nats.go" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) @@ -32,7 +33,7 @@ type KubePreUpgradeJob struct { frequency string } type KubescoreJob struct { - config *rest.Config + clientset *kubernetes.Clientset js nats.JetStreamContext frequency string } @@ -87,9 +88,9 @@ func (j *KubePreUpgradeJob) Run() { LogErr(err) } -func NewKubescoreJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*KubescoreJob, error) { +func NewKubescoreJob(clientset *kubernetes.Clientset, js nats.JetStreamContext, frequency string) (*KubescoreJob, error) { return &KubescoreJob{ - config: config, + clientset: clientset, js: js, frequency: frequency, }, nil @@ -100,7 +101,7 @@ func (v *KubescoreJob) CronSpec() string { func (j *KubescoreJob) Run() { // Call the Kubescore function with the provided config and js - err := RunKubeScore(j.config, j.js) + err := RunKubeScore(j.clientset, j.js) LogErr(err) } func NewRakkessJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*RakkessJob, error) { diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 0f10badc..49a34641 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -532,15 +532,6 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations currentTime := time.Now().UTC() - // if _, err := stmt.Exec( - // metrics.ID, - // metrics.Namespace, - // metrics.ClusterName, - // metrics.Recommendations, - // currentTime, - // ); err != nil { - // log.Fatal(err) - // } for _, result := range metrics.Report { for _, check := range result.Checks { for _, comments := range check.Comments { diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index e1abea47..3185fb53 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -237,7 +237,7 @@ const InsertDeletedApi DBStatement = "INSERT INTO DeletedAPIs (ClusterName, Obje const InsertKubvizEvent DBStatement = "INSERT INTO events (ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, FirstTime, LastTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const clickhouseExperimental DBStatement = `SET allow_experimental_object_type=1;` const containerGithubTable DBStatement = `CREATE table IF NOT EXISTS container_github(event JSON) ENGINE = MergeTree ORDER BY tuple();` -const InsertKubeScore string = "INSERT INTO kubescore (id, namespace, cluster_name, recommendations, EventTime) VALUES (?, ?, ?, ?, ?)" +const InsertKubeScore string = "INSERT INTO kubescore(id,clustername,object_name,kind,apiVersion,name,namespace,target_type,description,path,summary,file_name,file_row,EventTime) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)" const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?)" const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/sql/000008_kubescore.up.sql b/sql/000008_kubescore.up.sql index 662fab9b..b6ee3e3f 100644 --- a/sql/000008_kubescore.up.sql +++ b/sql/000008_kubescore.up.sql @@ -1,8 +1,17 @@ CREATE TABLE IF NOT EXISTS kubescore ( - id UUID, - namespace String, - cluster_name String, - recommendations String, + id UUID, + clustername String, + object_name String, + kind String, + apiVersion String, + name String, + namespace String, + target_type String, + description String, + path String, + summary String, + file_name String, + file_row BIGINT, EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() From ddb5f8bac00ebee254a6ab31f24645a9c71b8e6e Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Dec 2023 23:17:11 +0530 Subject: [PATCH 155/263] kubescore added --- sql/000008_kubescore.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/000008_kubescore.up.sql b/sql/000008_kubescore.up.sql index b6ee3e3f..811f2fb1 100644 --- a/sql/000008_kubescore.up.sql +++ b/sql/000008_kubescore.up.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS kubescore ( path String, summary String, file_name String, - file_row BIGINT, + file_row String, EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() From 7e97e6c577e251c4d059b5b9244426f156047258 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 19 Dec 2023 12:04:58 +0530 Subject: [PATCH 156/263] updated clickhouse dependency version --- charts/client/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index fb8dc1ef..a8aee4f4 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -29,7 +29,7 @@ dependencies: repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: clickhouse condition: clickhouse.enabled - version: 1.0.1 + version: 1.0.2 repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - name: grafana condition: grafana.enabled From b52abba7815be8ec343fb4de83de76a4a02029bf Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Dec 2023 15:12:37 +0530 Subject: [PATCH 157/263] fix --- sql/000008_kubescore.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/000008_kubescore.up.sql b/sql/000008_kubescore.up.sql index 811f2fb1..b6ee3e3f 100644 --- a/sql/000008_kubescore.up.sql +++ b/sql/000008_kubescore.up.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS kubescore ( path String, summary String, file_name String, - file_row String, + file_row BIGINT, EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() From e053714be6375cab1b93807af8b8cb0bb0558526 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Dec 2023 16:16:44 +0530 Subject: [PATCH 158/263] fix --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 49a34641..ab8f3bc9 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -549,7 +549,7 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations comments.Path, comments.Summary, result.FileName, - result.FileRow, + int64(result.FileRow), currentTime, ); err != nil { log.Println("Error while inserting KubeScore metrics:", err) From 304f29b266be6fc4b6e4d7271ebcd87e577e21b7 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 19 Dec 2023 16:50:46 +0530 Subject: [PATCH 159/263] fix --- agent/kubviz/k8smetrics_agent.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 64595314..c3e96693 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -119,17 +119,17 @@ func main() { go publishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { - // err := outDatedImages(config, js) - // LogErr(err) - // err = KubePreUpgradeDetector(config, js) - // LogErr(err) - // err = GetAllResources(config, js) - // LogErr(err) - // err = RakeesOutput(config, js) - // LogErr(err) + err := outDatedImages(config, js) + LogErr(err) + err = KubePreUpgradeDetector(config, js) + LogErr(err) + err = GetAllResources(config, js) + LogErr(err) + err = RakeesOutput(config, js) + LogErr(err) // //getK8sEvents(clientset) - // err = runTrivyScans(config, js) - // LogErr(err) + err = runTrivyScans(config, js) + LogErr(err) err = RunKubeScore(clientset, js) LogErr(err) } From 61af2731de805c5c10209bcaff5fbcace483201b Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Tue, 19 Dec 2023 19:22:09 +0530 Subject: [PATCH 160/263] Added new panels --- charts/client/Chart.yaml | 2 +- .../configmap-kubedata-dashboard.yaml | 178 +++++++++++++++--- grafana/kubeData-dashboard.json | 143 ++++++++++++-- 3 files changed, 281 insertions(+), 42 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index a8aee4f4..b9231523 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.12 +version: 1.1.13 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-kubedata-dashboard.yaml b/charts/client/templates/configmap-kubedata-dashboard.yaml index 9a263900..f5006544 100644 --- a/charts/client/templates/configmap-kubedata-dashboard.yaml +++ b/charts/client/templates/configmap-kubedata-dashboard.yaml @@ -32,21 +32,139 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 31, + "id": 146, "links": [], "liveNow": false, "panels": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { - "h": 14, + "h": 9, "w": 24, "x": 0, "y": 0 }, + "id": 11, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values;\n const namespaces = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const pods = data.series[0].fields[3].values;\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const reason = reasons[i];\n const pod = pods[i];\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const reasonNode = { name: reason, children: [{ name: `Count: ${pod}` }] };\n namespaceNode.children.push(reasonNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Namespace, Reason, count(*) AS Pods\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('Pod') AND ClusterName IN ($clusterName) AND Namespace IN ($namespace) AND Reason IN ($reason)\nGROUP BY ClusterName, Namespace, Reason", + "rawQuery": "SELECT ClusterName, Namespace, Reason, count(*) AS Pods\nFROM default.events\nWHERE EventTime >= toDateTime(1702949773) AND EventTime <= toDateTime(1702992973) AND Kind IN ('Pod') AND ClusterName IN ('capten-controlplane','kubviz','dev') AND Namespace IN ('argo-cd','crossplane','default','kubescape-prometheus','kubviz','kyverno','linkerd','observability','openebs-cstor','testkube','capten','falco','tekton','crossplane-system','test5','tek','tekton-pipelines','harbor','tekton-pipelines-resolvers','quality-trace','kube-system','cert-manager','emojivoto','local-path-storage','test-linkerd','external-secrets','policy-reporter','velero','tracetest') AND Reason IN ('BackOff','FailedMount','Unhealthy','SyncPackage','SelectComposition','SyncFailed','CannotUpdateExternalResource','RenderCRD','OfferClaim','EstablishComposite','SuccessfulCreate','InjectionSkipped','Scheduled','Pulled','Created','Started','Completed','SawCompletedJob','BindClusterRole','ApplyClusterRoles','CannotInitializeManagedResource','InstallPackageRevision','CreatedUsers','CreatedSuperuser','ApplyRoles','Synced','Killing','Init','ExternalProvisioning','Provisioning','Running','FailedScheduling','ProvisioningSucceeded','ScalingReplicaSet','Pending','Updated','FailUpdate','Degraded','Pulling','Healthy','Succeeded','IssuedLeafCertificate','Failed','Offline','EvictionThresholdMet','NodeHasDiskPressure','NodeHasNoDiskPressure','FreeDiskSpaceFailed','CreateCertificate','Issuing','Generated','Requested','cert-manager.io','OrderCreated','OrderPending','Presented','DomainVerified','Complete','CertificateIssued','SuccessfulDelete','ConfigureCompositeResource','BindCompositeResource','ImageGCFailed','ClaimLost','Evicted','RecreatingFailedPod','LeaderElection','InternalError','Reused','IssuerUpdated','OperationStarted','ResourceUpdated','OperationCompleted','MultiplePodDisruptionBudgets','MissingJob','InvalidOrder','TaintManagerEviction','SystemOOM','FailedKillPod','NodeHasSufficientMemory','NodeHasSufficientPID','NodeNotReady','FailedPreStopHook','NodeReady','Pool Imported','AlreadyPresent','StartingCassandra','UpdateCompleted','LabeledPodAsSeed','StartedCassandra','ComposeResources','UpdatedExternalResource','CannotDeleteExternalResource','DeletedExternalResource','ReconcileInProgress','ReconcileCompleted','ReconcileStarted','ProgressHostsCompleted','ReconcileFailed','UnlabeledPodAsSeed','CannotObserveExternalResource','DeletingStuckPod','DeleteCompositeResource','ResourceDeleted','PublishConnectionSecret','UnpackPackage','ResolveDependencies','ExceededGracePeriod','ProvisioningFailed','CannotCreateExternalResource','FailStatusSync','FailCreate','CannotUpdateManagedResource','CannotResolveResourceReferences','CreatedExternalResource','FailedSync','RegisteredNode','OwnerRefInvalidNamespace','NoPods','DeadlineExceeded','Create','FailedGetScale','NodeAllocatableEnforced','Starting','LintPackage','SandboxChanged','WaitForFirstConsumer','FailedGetResourceMetric','FailedCreate','Injected','Resizing','ExternalExpanding','VolumeResizeFailed','VolumeResizeSuccessful','FileSystemResizeRequired','FileSystemResizeSuccessful','CreatedResource','FailedToUpdateEndpoint','UpdateCertificate','FailedUpdateStatus','UpdateFailed','FailedComputeMetricsReplicas','FailedToCreateEndpoint','FailedAttachVolume','FailedToUpdateEndpointSlices','FailedDelete','Pool Expansion','Error')\nGROUP BY ClusterName, Namespace, Reason", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Pod Scenario Counts by Cluster, Namespace", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 12, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const reasons = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Check if reasons and counts are defined and not empty\n if (!reasons || !counts || reasons.length === 0 || counts.length === 0) {\n // Display a message when no data is available\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n } else {\n // Data is available, proceed with the chart creation\n // Create an array of data items, each containing name and value\n const seriesData = reasons.map((reason, index) => ({\n name: reason,\n value: counts[index],\n }));\n\n // Define a custom color for the bars\n const customColor = 'rgb(0, 123, 255)'; // Change this to your desired color\n\n // Apache ECharts option\n option = {\n xAxis: {\n type: 'category',\n data: reasons, // Use the reasons directly for xAxis data\n axisLabel: {\n interval: 0, // Display all labels on the xAxis\n },\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Pods'], // Legend name\n left: 'left', // Position the legend on the left side\n bottom: 'bottom', // Position the legend at the bottom\n },\n series: [\n {\n name: 'Pods', // Series name for the legend\n data: seriesData,\n type: 'bar',\n label: {\n show: true,\n position: 'top',\n formatter: '{c}',\n },\n itemStyle: {\n barBorderRadius: [5, 5, 0, 0], // Adjust the values to control the curvature\n color: customColor, // Set the custom color for the bars\n },\n },\n ],\n };\n }\n}\n\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Reason, count(Reason) AS Pods\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('Pod') AND ClusterName IN ($clusterName) AND Namespace In ($namespace)\nGROUP BY Reason", + "rawQuery": "SELECT Reason, count(Reason) AS Pods\nFROM default.events\nWHERE EventTime >= toDateTime(1702949951) AND EventTime <= toDateTime(1702993151) AND Kind IN ('Pod') AND ClusterName IN ('capten-controlplane','kubviz','dev') AND Namespace In ('quality-trace','crossplane-system','observability','default','testkube','openebs-cstor','kyverno','kubescape-prometheus','tekton-pipelines','capten','tek','test-linkerd','linkerd','argo-cd','tracetest','emojivoto','falco','kube-system','crossplane','kubviz','tekton','test5','harbor','tekton-pipelines-resolvers','cert-manager','local-path-storage','external-secrets','policy-reporter','velero')\nGROUP BY Reason", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Total Pod Counts by Reason", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 19 + }, "id": 6, "options": { "baidu": { @@ -78,7 +196,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -98,13 +216,13 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 14 + "y": 33 }, "id": 8, "options": { @@ -137,7 +255,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -157,13 +275,13 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 14 + "y": 33 }, "id": 9, "options": { @@ -196,7 +314,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -216,13 +334,13 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 13, "w": 24, "x": 0, - "y": 22 + "y": 41 }, "id": 7, "options": { @@ -238,7 +356,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Cluster Hierarchy',\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n const option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Start directly from the Cluster Hierarchy node\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Root', // You can customize the name of the root node if needed\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly as root nodes\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -255,7 +373,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -275,13 +393,13 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { "h": 11, "w": 24, "x": 0, - "y": 35 + "y": 54 }, "id": 5, "options": { @@ -314,7 +432,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -334,7 +452,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "description": "This stats panel shows the pod creation events.", "fieldConfig": { @@ -348,7 +466,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -363,7 +482,7 @@ data: "h": 7, "w": 24, "x": 0, - "y": 46 + "y": 65 }, "id": 4, "options": { @@ -385,7 +504,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -405,7 +524,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "description": "This table panal shows all the kubernetes datas.\nif its not showing the data check the time range and refresh.it will take some time to load the data. wait for sometime before clicking refresh another time.", "fieldConfig": { @@ -441,7 +560,7 @@ data: "h": 16, "w": 24, "x": 0, - "y": 53 + "y": 72 }, "id": 2, "options": { @@ -461,7 +580,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -493,7 +612,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "definition": "SELECT Namespace FROM default.events", "hide": 0, @@ -516,7 +635,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "definition": "SELECT Reason FROM default.events", "hide": 0, @@ -539,7 +658,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "definition": "SELECT Kind FROM default.events", "hide": 0, @@ -562,7 +681,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" + "uid": "vertamedia-clickhouse-datasource" }, "definition": "SELECT ClusterName FROM default.events", "hide": 0, @@ -587,7 +706,8 @@ data: "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 1, + "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/grafana/kubeData-dashboard.json b/grafana/kubeData-dashboard.json index e253f234..5b351bf5 100644 --- a/grafana/kubeData-dashboard.json +++ b/grafana/kubeData-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 31, + "id": 146, "links": [], "liveNow": false, "panels": [ @@ -31,11 +31,129 @@ "uid": "vertamedia-clickhouse-datasource" }, "gridPos": { - "h": 14, + "h": 9, "w": 24, "x": 0, "y": 0 }, + "id": 11, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values;\n const namespaces = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const pods = data.series[0].fields[3].values;\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const reason = reasons[i];\n const pod = pods[i];\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const reasonNode = { name: reason, children: [{ name: `Count: ${pod}` }] };\n namespaceNode.children.push(reasonNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT ClusterName, Namespace, Reason, count(*) AS Pods\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('Pod') AND ClusterName IN ($clusterName) AND Namespace IN ($namespace) AND Reason IN ($reason)\nGROUP BY ClusterName, Namespace, Reason", + "rawQuery": "SELECT ClusterName, Namespace, Reason, count(*) AS Pods\nFROM default.events\nWHERE EventTime >= toDateTime(1702949773) AND EventTime <= toDateTime(1702992973) AND Kind IN ('Pod') AND ClusterName IN ('capten-controlplane','kubviz','dev') AND Namespace IN ('argo-cd','crossplane','default','kubescape-prometheus','kubviz','kyverno','linkerd','observability','openebs-cstor','testkube','capten','falco','tekton','crossplane-system','test5','tek','tekton-pipelines','harbor','tekton-pipelines-resolvers','quality-trace','kube-system','cert-manager','emojivoto','local-path-storage','test-linkerd','external-secrets','policy-reporter','velero','tracetest') AND Reason IN ('BackOff','FailedMount','Unhealthy','SyncPackage','SelectComposition','SyncFailed','CannotUpdateExternalResource','RenderCRD','OfferClaim','EstablishComposite','SuccessfulCreate','InjectionSkipped','Scheduled','Pulled','Created','Started','Completed','SawCompletedJob','BindClusterRole','ApplyClusterRoles','CannotInitializeManagedResource','InstallPackageRevision','CreatedUsers','CreatedSuperuser','ApplyRoles','Synced','Killing','Init','ExternalProvisioning','Provisioning','Running','FailedScheduling','ProvisioningSucceeded','ScalingReplicaSet','Pending','Updated','FailUpdate','Degraded','Pulling','Healthy','Succeeded','IssuedLeafCertificate','Failed','Offline','EvictionThresholdMet','NodeHasDiskPressure','NodeHasNoDiskPressure','FreeDiskSpaceFailed','CreateCertificate','Issuing','Generated','Requested','cert-manager.io','OrderCreated','OrderPending','Presented','DomainVerified','Complete','CertificateIssued','SuccessfulDelete','ConfigureCompositeResource','BindCompositeResource','ImageGCFailed','ClaimLost','Evicted','RecreatingFailedPod','LeaderElection','InternalError','Reused','IssuerUpdated','OperationStarted','ResourceUpdated','OperationCompleted','MultiplePodDisruptionBudgets','MissingJob','InvalidOrder','TaintManagerEviction','SystemOOM','FailedKillPod','NodeHasSufficientMemory','NodeHasSufficientPID','NodeNotReady','FailedPreStopHook','NodeReady','Pool Imported','AlreadyPresent','StartingCassandra','UpdateCompleted','LabeledPodAsSeed','StartedCassandra','ComposeResources','UpdatedExternalResource','CannotDeleteExternalResource','DeletedExternalResource','ReconcileInProgress','ReconcileCompleted','ReconcileStarted','ProgressHostsCompleted','ReconcileFailed','UnlabeledPodAsSeed','CannotObserveExternalResource','DeletingStuckPod','DeleteCompositeResource','ResourceDeleted','PublishConnectionSecret','UnpackPackage','ResolveDependencies','ExceededGracePeriod','ProvisioningFailed','CannotCreateExternalResource','FailStatusSync','FailCreate','CannotUpdateManagedResource','CannotResolveResourceReferences','CreatedExternalResource','FailedSync','RegisteredNode','OwnerRefInvalidNamespace','NoPods','DeadlineExceeded','Create','FailedGetScale','NodeAllocatableEnforced','Starting','LintPackage','SandboxChanged','WaitForFirstConsumer','FailedGetResourceMetric','FailedCreate','Injected','Resizing','ExternalExpanding','VolumeResizeFailed','VolumeResizeSuccessful','FileSystemResizeRequired','FileSystemResizeSuccessful','CreatedResource','FailedToUpdateEndpoint','UpdateCertificate','FailedUpdateStatus','UpdateFailed','FailedComputeMetricsReplicas','FailedToCreateEndpoint','FailedAttachVolume','FailedToUpdateEndpointSlices','FailedDelete','Pool Expansion','Error')\nGROUP BY ClusterName, Namespace, Reason", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Pod Scenario Counts by Cluster, Namespace", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 12, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const reasons = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Check if reasons and counts are defined and not empty\n if (!reasons || !counts || reasons.length === 0 || counts.length === 0) {\n // Display a message when no data is available\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n } else {\n // Data is available, proceed with the chart creation\n // Create an array of data items, each containing name and value\n const seriesData = reasons.map((reason, index) => ({\n name: reason,\n value: counts[index],\n }));\n\n // Define a custom color for the bars\n const customColor = 'rgb(0, 123, 255)'; // Change this to your desired color\n\n // Apache ECharts option\n option = {\n xAxis: {\n type: 'category',\n data: reasons, // Use the reasons directly for xAxis data\n axisLabel: {\n interval: 0, // Display all labels on the xAxis\n },\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Pods'], // Legend name\n left: 'left', // Position the legend on the left side\n bottom: 'bottom', // Position the legend at the bottom\n },\n series: [\n {\n name: 'Pods', // Series name for the legend\n data: seriesData,\n type: 'bar',\n label: {\n show: true,\n position: 'top',\n formatter: '{c}',\n },\n itemStyle: {\n barBorderRadius: [5, 5, 0, 0], // Adjust the values to control the curvature\n color: customColor, // Set the custom color for the bars\n },\n },\n ],\n };\n }\n}\n\nreturn option;", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT Reason, count(Reason) AS Pods\nFROM default.events\nWHERE $timeFilterByColumn(EventTime) AND Kind IN ('Pod') AND ClusterName IN ($clusterName) AND Namespace In ($namespace)\nGROUP BY Reason", + "rawQuery": "SELECT Reason, count(Reason) AS Pods\nFROM default.events\nWHERE EventTime >= toDateTime(1702949951) AND EventTime <= toDateTime(1702993151) AND Kind IN ('Pod') AND ClusterName IN ('capten-controlplane','kubviz','dev') AND Namespace In ('quality-trace','crossplane-system','observability','default','testkube','openebs-cstor','kyverno','kubescape-prometheus','tekton-pipelines','capten','tek','test-linkerd','linkerd','argo-cd','tracetest','emojivoto','falco','kube-system','crossplane','kubviz','tekton','test5','harbor','tekton-pipelines-resolvers','cert-manager','local-path-storage','external-secrets','policy-reporter','velero')\nGROUP BY Reason", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Total Pod Counts by Reason", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 19 + }, "id": 6, "options": { "baidu": { @@ -93,7 +211,7 @@ "h": 8, "w": 12, "x": 0, - "y": 14 + "y": 33 }, "id": 8, "options": { @@ -152,7 +270,7 @@ "h": 8, "w": 12, "x": 12, - "y": 14 + "y": 33 }, "id": 9, "options": { @@ -211,7 +329,7 @@ "h": 13, "w": 24, "x": 0, - "y": 22 + "y": 41 }, "id": 7, "options": { @@ -227,7 +345,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Cluster Hierarchy',\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n const option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Start directly from the Cluster Hierarchy node\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Root', // You can customize the name of the root node if needed\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly as root nodes\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -270,7 +388,7 @@ "h": 11, "w": 24, "x": 0, - "y": 35 + "y": 54 }, "id": 5, "options": { @@ -337,7 +455,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -352,7 +471,7 @@ "h": 7, "w": 24, "x": 0, - "y": 46 + "y": 65 }, "id": 4, "options": { @@ -430,7 +549,7 @@ "h": 16, "w": 24, "x": 0, - "y": 53 + "y": 72 }, "id": 2, "options": { @@ -576,6 +695,6 @@ "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 1, + "version": 2, "weekStart": "" -} \ No newline at end of file +} From 5cbf35c7fb590fdba77d0ab2261169ec98affa98 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Tue, 19 Dec 2023 19:25:40 +0530 Subject: [PATCH 161/263] fix --- .../configmap-kubedata-dashboard.yaml | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/charts/client/templates/configmap-kubedata-dashboard.yaml b/charts/client/templates/configmap-kubedata-dashboard.yaml index f5006544..7e275444 100644 --- a/charts/client/templates/configmap-kubedata-dashboard.yaml +++ b/charts/client/templates/configmap-kubedata-dashboard.yaml @@ -39,7 +39,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 9, @@ -78,7 +78,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -98,7 +98,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 10, @@ -137,7 +137,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -157,7 +157,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 14, @@ -196,7 +196,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -216,7 +216,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 8, @@ -255,7 +255,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -275,7 +275,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 8, @@ -314,7 +314,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -334,7 +334,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 13, @@ -373,7 +373,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -393,7 +393,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "gridPos": { "h": 11, @@ -432,7 +432,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -452,7 +452,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "description": "This stats panel shows the pod creation events.", "fieldConfig": { @@ -504,7 +504,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -524,7 +524,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "description": "This table panal shows all the kubernetes datas.\nif its not showing the data check the time range and refresh.it will take some time to load the data. wait for sometime before clicking refresh another time.", "fieldConfig": { @@ -580,7 +580,7 @@ data: { "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", "extrapolate": true, @@ -612,7 +612,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "definition": "SELECT Namespace FROM default.events", "hide": 0, @@ -635,7 +635,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "definition": "SELECT Reason FROM default.events", "hide": 0, @@ -658,7 +658,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "definition": "SELECT Kind FROM default.events", "hide": 0, @@ -681,7 +681,7 @@ data: }, "datasource": { "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" + "uid": "{{ .Values.datasources.uid }}" }, "definition": "SELECT ClusterName FROM default.events", "hide": 0, From a291bdab4b070de941ad98867860279ab85cdb3b Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 19 Dec 2023 19:33:50 +0530 Subject: [PATCH 162/263] cache temp fix --- agent/kubviz/trivy.go | 12 ++++++------ agent/kubviz/trivy_image.go | 12 ++++++------ agent/kubviz/trivy_sbom.go | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 04ee6f81..848f2595 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -40,7 +40,7 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { } var report report.ConsolidatedReport cmdString := fmt.Sprintf("trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir %s --debug", trivyCacheDir) - clearCacheCmd := "trivy k8s --clear-cache" + // clearCacheCmd := "trivy k8s --clear-cache" out, err := executeCommandTrivy(cmdString) if err != nil { log.Printf("Error executing command: %v\n", err) @@ -62,11 +62,11 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { log.Printf("Error occurred while Unmarshalling json for k8s cluster scan: %v", err) return err } - _, err = executeCommandTrivy(clearCacheCmd) - if err != nil { - log.Printf("Error executing command: %v\n", err) - return err - } + // _, err = executeCommandTrivy(clearCacheCmd) + // if err != nil { + // log.Printf("Error executing command: %v\n", err) + // return err + // } err = publishTrivyK8sReport(report, js) if err != nil { return err diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index ff2e9847..8a64fb5c 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -23,7 +23,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error creating Trivy Image cache directory: %v\n", err) return err } - clearCacheCmd := "trivy image --clear-cache" + // clearCacheCmd := "trivy image --clear-cache" images, err := ListImages(config) if err != nil { @@ -56,11 +56,11 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error occurred while Unmarshalling json for image: %v", err) continue // Move on to the next image in case of an error } - _, err = executeCommandTrivy(clearCacheCmd) - if err != nil { - log.Printf("Error executing command: %v\n", err) - return err - } + // _, err = executeCommandTrivy(clearCacheCmd) + // if err != nil { + // log.Printf("Error executing command: %v\n", err) + // return err + // } err = publishImageScanReports(report, js) if err != nil { return err diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 98113af4..d83ba649 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -54,7 +54,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error creating Trivy cache directory: %v\n", err) return err } - clearCacheCmd := "trivy image --clear-cache" + // clearCacheCmd := "trivy image --clear-cache" log.Println("trivy sbom run started") images, err := ListImages(config) @@ -85,11 +85,11 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image in case of an error } // log.Println("report", report) - _, err = executeCommandTrivy(clearCacheCmd) - if err != nil { - log.Printf("Error executing command: %v\n", err) - return err - } + // _, err = executeCommandTrivy(clearCacheCmd) + // if err != nil { + // log.Printf("Error executing command: %v\n", err) + // return err + // } // Publish the report using the given function publishTrivySbomReport(report, js) } From b52b549947c8d0fe24d9e718f0181a86ae5b0194 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 20 Dec 2023 10:04:50 +0530 Subject: [PATCH 163/263] sql-fixed --- client/pkg/clickhouse/statements.go | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 3185fb53..02770d7f 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -205,26 +205,14 @@ const quayContainerPushEventTable DBStatement = ` const trivySbomTable DBStatement = ` CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, - schema String, - bom_format String, - spec_version String, - serial_number String, + image_name String, + package_url String, + bom_ref String, + serial_number String, version INTEGER, - metadata_timestamp DateTime('UTC'), - metatool_vendor String, - metatool_name String, - metatool_version String, - component_bom_ref String, - component_type String, - component_name String, + bom_format String, component_version String, - component_property_name String, - component_property_value String, - component_hash_alg String, - component_hash_content String, - component_license_exp String, - component_purl String, - dependency_ref String + component_mime_type String ) engine=File(TabSeparated) ` @@ -242,6 +230,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, package_url, bom_ref, serial_number, version, bom_format, component_version, component_mimetype) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, package_url, bom_ref, serial_number, version, bom_format, component_version, component_mime_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" From 6072aec2263abc034c7cc1e1ec492a06bf59190c Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 20 Dec 2023 10:44:17 +0530 Subject: [PATCH 164/263] int-conversion --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index ab8f3bc9..7d102a15 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -705,7 +705,7 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { result.CycloneDX.Metadata.Component.PackageURL, result.CycloneDX.Metadata.Component.BOMRef, result.CycloneDX.SerialNumber, - result.CycloneDX.Version, + int32(result.CycloneDX.Version), result.CycloneDX.BOMFormat, result.CycloneDX.Metadata.Component.Version, result.CycloneDX.Metadata.Component.MIMEType, From 9d777be3dd9f9fbaf0653dab5f56bd7e5abe71c4 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 20 Dec 2023 10:46:58 +0530 Subject: [PATCH 165/263] int-conversion2 --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index c3e96693..77d21097 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -64,11 +64,11 @@ var ( ) func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { - err := RunTrivyImageScans(config, js) + err := RunTrivySbomScan(config, js) if err != nil { return err } - err = RunTrivySbomScan(config, js) + err = RunTrivyImageScans(config, js) if err != nil { return err } From 385b69efae30de708adb4578ecf42327429695cc Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 20 Dec 2023 11:33:28 +0530 Subject: [PATCH 166/263] Add podSecurityContext --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index dfadb805..67773fc7 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.8 +version: 1.1.9 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 48aa6710..f431427d 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -27,8 +27,10 @@ serviceAccount: podAnnotations: {} -podSecurityContext: {} - # fsGroup: 2000 +podSecurityContext: + fsGroup: 1001 + runAsUser: 1001 + runAsGroup: 1001 securityContext: {} # capabilities: From 1e49f04176c10af64e318d5ba574ec3c7c6fd52b Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 21 Dec 2023 17:23:52 +0530 Subject: [PATCH 167/263] bom-data --- agent/kubviz/k8smetrics_agent.go | 24 ++++------------- agent/kubviz/scheduler_watch.go | 2 +- agent/kubviz/trivy_sbom.go | 40 ++++++++++++++--------------- client/pkg/clickhouse/db_client.go | 25 ++++++------------ client/pkg/clickhouse/statements.go | 6 ++--- client/pkg/clients/kubviz_client.go | 5 ++-- model/trivy_sbom.go | 10 ++++++++ sql/0000015_trivysbom.up.sql | 2 -- 8 files changed, 49 insertions(+), 65 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 77d21097..fbc9b24c 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -63,24 +63,6 @@ var ( schedulingIntervalStr string = os.Getenv("SCHEDULING_INTERVAL") ) -func runTrivyScans(config *rest.Config, js nats.JetStreamContext) error { - err := RunTrivySbomScan(config, js) - if err != nil { - return err - } - err = RunTrivyImageScans(config, js) - if err != nil { - return err - } - err = RunTrivyK8sClusterScan(js) - if err != nil { - return err - } - - return nil - -} - func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production @@ -128,7 +110,11 @@ func main() { err = RakeesOutput(config, js) LogErr(err) // //getK8sEvents(clientset) - err = runTrivyScans(config, js) + err = RunTrivySbomScan(config, js) + LogErr(err) + err = RunTrivyImageScans(config, js) + LogErr(err) + err = RunTrivyK8sClusterScan(js) LogErr(err) err = RunKubeScore(clientset, js) LogErr(err) diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler_watch.go index 6683816d..5c35ba5d 100644 --- a/agent/kubviz/scheduler_watch.go +++ b/agent/kubviz/scheduler_watch.go @@ -133,6 +133,6 @@ func (v *TrivyJob) CronSpec() string { func (j *TrivyJob) Run() { // Call the Trivy function with the provided config and js - err := runTrivyScans(j.config, j.js) + err := RunTrivySbomScan(j.config, j.js) LogErr(err) } diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index d83ba649..bbd5cb03 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -17,17 +17,26 @@ import ( ) func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { - metrics := model.Sbom{ - ID: uuid.New().String(), - Report: report, + metrics := model.SbomData{ + ID: uuid.New().String(), + ComponentName: report.CycloneDX.Metadata.Component.Name, + PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, + BomRef: report.CycloneDX.Metadata.Component.BOMRef, + SerialNumber: report.CycloneDX.SerialNumber, + CycloneDxVersion: report.CycloneDX.Version, + BomFormat: report.CycloneDX.BOMFormat, } - metricsJson, _ := json.Marshal(metrics) - _, err := js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) + metricsJson, err := json.Marshal(metrics) + if err!=nil { + log.Println("error occurred while marshalling sbom metrics in agent", err.Error()) + return err + } + _, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) if err != nil { return err } - log.Printf("Trivy report with Id %v has been published\n", metrics.ID) + log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) return nil } @@ -36,17 +45,15 @@ func executeCommandSbom(command string) ([]byte, error) { var outc, errc bytes.Buffer cmd.Stdout = &outc cmd.Stderr = &errc - err := cmd.Run() - if err != nil { log.Println("Execute SBOM Command Error", err.Error()) } - return outc.Bytes(), err } func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { + log.Println("trivy sbom scan started...") pvcMountPath := "/mnt/agent/kbz" trivySbomCacheDir := fmt.Sprintf("%s/trivy-sbomcache", pvcMountPath) err := os.MkdirAll(trivySbomCacheDir, 0755) @@ -54,9 +61,6 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error creating Trivy cache directory: %v\n", err) return err } - // clearCacheCmd := "trivy image --clear-cache" - - log.Println("trivy sbom run started") images, err := ListImages(config) if err != nil { @@ -71,7 +75,10 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error executing Trivy for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error } - + if out == nil { + log.Printf("Trivy output is nil for image sbom %s", image.PullableImage) + continue + } // Check if the output is empty or invalid JSON if len(out) == 0 { log.Printf("Trivy output is empty for image sbom %s", image.PullableImage) @@ -84,13 +91,6 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error unmarshaling JSON data for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error } - // log.Println("report", report) - // _, err = executeCommandTrivy(clearCacheCmd) - // if err != nil { - // log.Printf("Error executing command: %v\n", err) - // return err - // } - // Publish the report using the given function publishTrivySbomReport(report, js) } return nil diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 7d102a15..4e21d0e2 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -33,7 +33,7 @@ type DBInterface interface { InsertGitEvent(string) InsertKubeScoreMetrics(model.KubeScoreRecommendations) InsertTrivyImageMetrics(metrics model.TrivyImage) - InsertTrivySbomMetrics(metrics model.Sbom) + InsertTrivySbomMetrics(metrics model.SbomData) InsertTrivyMetrics(metrics model.Trivy) RetriveKetallEvent() ([]model.Resource, error) RetriveOutdatedEvent() ([]model.CheckResultfinal, error) @@ -685,11 +685,9 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { } } -func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { +func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { log.Println("####started inserting value") - result := metrics.Report - if result.CycloneDX != nil { tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -701,14 +699,12 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { if _, err := stmt.Exec( metrics.ID, - result.CycloneDX.Metadata.Component.Name, - result.CycloneDX.Metadata.Component.PackageURL, - result.CycloneDX.Metadata.Component.BOMRef, - result.CycloneDX.SerialNumber, - int32(result.CycloneDX.Version), - result.CycloneDX.BOMFormat, - result.CycloneDX.Metadata.Component.Version, - result.CycloneDX.Metadata.Component.MIMEType, + metrics.ComponentName, + metrics.PackageUrl, + metrics.BomRef, + metrics.SerialNumber, + int32(metrics.CycloneDxVersion), + metrics.BomFormat, ); err != nil { log.Fatal(err) } @@ -716,11 +712,6 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { log.Fatal(err) } stmt.Close() - } else { - log.Println("sbom payload not available for db insertion, skipping db insertion") - - } - } func (c *DBClient) Close() { _ = c.conn.Close() diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 02770d7f..797cb061 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -210,9 +210,7 @@ const trivySbomTable DBStatement = ` bom_ref String, serial_number String, version INTEGER, - bom_format String, - component_version String, - component_mime_type String + bom_format String ) engine=File(TabSeparated) ` @@ -230,6 +228,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, package_url, bom_ref, serial_number, version, bom_format, component_version, component_mime_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index e2208a4a..683cd563 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -118,10 +118,11 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { Consumer: constants.Trivy_Sbom_Consumer, Handler: func(msg *nats.Msg) { msg.Ack() - var metrics model.Sbom + var metrics model.SbomData err := json.Unmarshal(msg.Data, &metrics) if err != nil { - log.Println("failed to unmarshal in nats", err) + log.Println("failed to unmarshal from nats", err) + return } log.Printf("Trivy sbom Metrics Received: %#v,", metrics) conn.InsertTrivySbomMetrics(metrics) diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index c6e6c850..8eea1769 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -9,4 +9,14 @@ type Sbom struct { Report cyclonedx.BOM } +type SbomData struct { + ID string + ComponentName string + PackageUrl string + BomRef string + SerialNumber string + CycloneDxVersion int + BomFormat string +} + diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index 163478cd..924f9ec8 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -6,8 +6,6 @@ CREATE TABLE IF NOT EXISTS trivysbom ( serial_number String, version INTEGER, bom_format String, - component_version String, - component_mime_type String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() ORDER BY ExpiryDate From 15548a72079e7b210da736fec8c81b3388902285 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 21 Dec 2023 17:52:02 +0530 Subject: [PATCH 168/263] scheduler-added-trivy --- agent/kubviz/scheduler_watch.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler_watch.go index 5c35ba5d..90f9f0dc 100644 --- a/agent/kubviz/scheduler_watch.go +++ b/agent/kubviz/scheduler_watch.go @@ -135,4 +135,8 @@ func (j *TrivyJob) Run() { // Call the Trivy function with the provided config and js err := RunTrivySbomScan(j.config, j.js) LogErr(err) + err = RunTrivyImageScans(j.config, j.js) + LogErr(err) + err = RunTrivyK8sClusterScan(j.js) + LogErr(err) } From 4b4d8c5b6cb7c4d163ea3c550b1512e080f59c6d Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Thu, 21 Dec 2023 20:50:34 +0530 Subject: [PATCH 169/263] sbom table fix --- charts/client/Chart.yaml | 2 +- .../templates/configmap-trivy-dashboard.yaml | 28 ++++--------------- grafana/trivy-dashboard.json | 28 ++++--------------- 3 files changed, 11 insertions(+), 47 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index b9231523..5b575474 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.13 +version: 1.1.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 314e613b..7d152057 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 125, + "id": 171, "links": [], "liveNow": false, "panels": [ @@ -166,7 +166,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -237,7 +236,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -305,7 +303,6 @@ data: "fieldConfig": { "defaults": { "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "percentage", "steps": [ @@ -374,7 +371,6 @@ data: "fieldConfig": { "defaults": { "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "percentage", "steps": [ @@ -446,7 +442,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -517,7 +512,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -588,7 +582,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -659,7 +652,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -730,7 +722,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -801,7 +792,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -872,7 +862,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -943,7 +932,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1015,7 +1003,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1086,7 +1073,6 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1164,7 +1150,6 @@ data: "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1237,7 +1222,6 @@ data: "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1281,8 +1265,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", - "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694156566) AND EventTime <= toDateTime(1694242966)", "refId": "A", "round": "0s", "skip_comments": true @@ -1310,7 +1294,6 @@ data: "inspect": false }, "mappings": [], - "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1383,7 +1366,6 @@ data: "inspect": false }, "mappings": [], - "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1427,8 +1409,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE $timeFilterByColumn(metadata_timestamp)\nORDER BY metadata_timestamp DESC", - "rawQuery": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE metadata_timestamp >= toDateTime(1693581733) AND metadata_timestamp <= toDateTime(1694186533)\nORDER BY metadata_timestamp DESC", + "query": "SELECT * FROM default.trivysbom", + "rawQuery": "SELECT * FROM default.trivysbom", "refId": "A", "round": "0s", "skip_comments": true diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 21017e18..5162ad23 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 125, + "id": 171, "links": [], "liveNow": false, "panels": [ @@ -155,7 +155,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -226,7 +225,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -294,7 +292,6 @@ "fieldConfig": { "defaults": { "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "percentage", "steps": [ @@ -363,7 +360,6 @@ "fieldConfig": { "defaults": { "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "percentage", "steps": [ @@ -435,7 +431,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -506,7 +501,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -577,7 +571,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -648,7 +641,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -719,7 +711,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -790,7 +781,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -861,7 +851,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -932,7 +921,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1004,7 +992,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1075,7 +1062,6 @@ "mode": "continuous-GrYlRd" }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1153,7 +1139,6 @@ "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1226,7 +1211,6 @@ "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1270,8 +1254,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", - "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694156566) AND EventTime <= toDateTime(1694242966)", "refId": "A", "round": "0s", "skip_comments": true @@ -1299,7 +1283,6 @@ "inspect": false }, "mappings": [], - "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1372,7 +1355,6 @@ "inspect": false }, "mappings": [], - "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1416,8 +1398,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE $timeFilterByColumn(metadata_timestamp)\nORDER BY metadata_timestamp DESC", - "rawQuery": "SELECT \"schema\", \"bom_format\", \"spec_version\", \"serial_number\", \"version\", \"metadata_timestamp\", \"metatool_vendor\", \"metatool_name\", \"metatool_version\", \"component_bom_ref\", \"component_type\", \"component_name\", \"component_version\", \"component_property_name\", \"component_property_value\", \"component_hash_alg\", \"component_hash_content\", \"component_license_exp\", \"component_purl\", \"dependency_ref\" \nFROM \"default\".\"trivysbom\"\nWHERE metadata_timestamp >= toDateTime(1693581733) AND metadata_timestamp <= toDateTime(1694186533)\nORDER BY metadata_timestamp DESC", + "query": "SELECT * FROM default.trivysbom", + "rawQuery": "SELECT * FROM default.trivysbom", "refId": "A", "round": "0s", "skip_comments": true From dac8eb47f21447ec55846cb4e9d387d38789ef46 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Thu, 21 Dec 2023 21:39:41 +0530 Subject: [PATCH 170/263] fix --- .../templates/configmap-trivy-dashboard.yaml | 24 ++++++++++++++++--- grafana/trivy-dashboard.json | 24 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 7d152057..8589def1 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 171, + "id": 170, "links": [], "liveNow": false, "panels": [ @@ -166,6 +166,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -236,6 +237,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -303,6 +305,7 @@ data: "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "percentage", "steps": [ @@ -371,6 +374,7 @@ data: "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "percentage", "steps": [ @@ -442,6 +446,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -512,6 +517,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -582,6 +588,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -652,6 +659,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -722,6 +730,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -792,6 +801,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -862,6 +872,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -932,6 +943,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1003,6 +1015,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1073,6 +1086,7 @@ data: "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1150,6 +1164,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1222,6 +1237,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1265,8 +1281,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694156566) AND EventTime <= toDateTime(1694242966)", + "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -1294,6 +1310,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1366,6 +1383,7 @@ data: "inspect": false }, "mappings": [], + "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 5162ad23..3bb3deef 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 171, + "id": 170, "links": [], "liveNow": false, "panels": [ @@ -155,6 +155,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -225,6 +226,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -292,6 +294,7 @@ "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "percentage", "steps": [ @@ -360,6 +363,7 @@ "fieldConfig": { "defaults": { "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "percentage", "steps": [ @@ -431,6 +435,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -501,6 +506,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -571,6 +577,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -641,6 +648,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -711,6 +719,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -781,6 +790,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -851,6 +861,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -921,6 +932,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -992,6 +1004,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1062,6 +1075,7 @@ "mode": "continuous-GrYlRd" }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1139,6 +1153,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1211,6 +1226,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1254,8 +1270,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)", - "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694156566) AND EventTime <= toDateTime(1694242966)", + "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -1283,6 +1299,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ @@ -1355,6 +1372,7 @@ "inspect": false }, "mappings": [], + "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ From 1aec2ef7368c193a6ce8d214c6d2d01f6802af6d Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 22 Dec 2023 12:16:54 +0530 Subject: [PATCH 171/263] clustername --- agent/kubviz/trivy_sbom.go | 7 +++++++ client/pkg/clickhouse/db_client.go | 3 +++ client/pkg/clickhouse/statements.go | 3 ++- model/trivy_sbom.go | 1 + sql/0000015_trivysbom.up.sql | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index bbd5cb03..7c93ada9 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -19,6 +19,7 @@ import ( func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { metrics := model.SbomData{ ID: uuid.New().String(), + ClusterName: ClusterName, ComponentName: report.CycloneDX.Metadata.Component.Name, PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, BomRef: report.CycloneDX.Metadata.Component.BOMRef, @@ -92,6 +93,12 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image in case of an error } publishTrivySbomReport(report, js) + + for _, packageInfo := range report.Packages { + for _, pkg := range packageInfo.Packages { + log.Printf("****Package name: %#v", pkg.Name) + } + } } return nil } diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 4e21d0e2..40ec1c1b 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -699,6 +699,7 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { if _, err := stmt.Exec( metrics.ID, + metrics.ClusterName, metrics.ComponentName, metrics.PackageUrl, metrics.BomRef, @@ -712,6 +713,8 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { log.Fatal(err) } stmt.Close() + + log.Printf("**Clustername :%#v",metrics.ClusterName) } func (c *DBClient) Close() { _ = c.conn.Close() diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 797cb061..d3fdfa10 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -205,6 +205,7 @@ const quayContainerPushEventTable DBStatement = ` const trivySbomTable DBStatement = ` CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, + cluster_name String, image_name String, package_url String, bom_ref String, @@ -228,6 +229,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, image_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index 8eea1769..9e68e610 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -11,6 +11,7 @@ type Sbom struct { type SbomData struct { ID string + ClusterName string ComponentName string PackageUrl string BomRef string diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index 924f9ec8..be0eb995 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -1,5 +1,6 @@ CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, + cluster_name String image_name String, package_url String, bom_ref String, From 2e75a06ba3cec402e0837591e660c3134e989222 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 22 Dec 2023 12:40:44 +0530 Subject: [PATCH 172/263] clusternam1 --- agent/kubviz/trivy_sbom.go | 7 +------ model/trivy_sbom.go | 1 + sql/0000015_trivysbom.up.sql | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 7c93ada9..38c8f0df 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -17,6 +17,7 @@ import ( ) func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { + metrics := model.SbomData{ ID: uuid.New().String(), ClusterName: ClusterName, @@ -93,12 +94,6 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image in case of an error } publishTrivySbomReport(report, js) - - for _, packageInfo := range report.Packages { - for _, pkg := range packageInfo.Packages { - log.Printf("****Package name: %#v", pkg.Name) - } - } } return nil } diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index 9e68e610..647e34a4 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -13,6 +13,7 @@ type SbomData struct { ID string ClusterName string ComponentName string + PackageName string PackageUrl string BomRef string SerialNumber string diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index be0eb995..d717d65c 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, - cluster_name String + cluster_name String, image_name String, package_url String, bom_ref String, From 212b259208a01b70e8f71a5dad6e24af00efda3c Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 22 Dec 2023 13:18:43 +0530 Subject: [PATCH 173/263] clusternam2 --- client/pkg/clickhouse/statements.go | 2 +- model/trivy_sbom.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index d3fdfa10..18c8c1d5 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -229,6 +229,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index 647e34a4..9e68e610 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -13,7 +13,6 @@ type SbomData struct { ID string ClusterName string ComponentName string - PackageName string PackageUrl string BomRef string SerialNumber string From 105b8ab461503445d90da633ddd8311110b1b057 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 22 Dec 2023 16:03:56 +0530 Subject: [PATCH 174/263] pckname --- agent/kubviz/trivy_sbom.go | 11 ++++++++++- client/pkg/clickhouse/db_client.go | 1 + client/pkg/clickhouse/statements.go | 3 ++- model/trivy_sbom.go | 1 + sql/0000015_trivysbom.up.sql | 1 + 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 38c8f0df..ff1b6bf5 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -18,10 +18,16 @@ import ( func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { + for _,packageinfo :=range report.Packages { + for _, pkg := range packageinfo.Packages { + //log.Println(pkg.Name) + + metrics := model.SbomData{ ID: uuid.New().String(), ClusterName: ClusterName, ComponentName: report.CycloneDX.Metadata.Component.Name, + PackageName: pkg.Name, PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, BomRef: report.CycloneDX.Metadata.Component.BOMRef, SerialNumber: report.CycloneDX.SerialNumber, @@ -37,8 +43,11 @@ func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) erro if err != nil { return err } - log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) + +} +} + return nil } diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 40ec1c1b..377fffe5 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -701,6 +701,7 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { metrics.ID, metrics.ClusterName, metrics.ComponentName, + metrics.PackageName, metrics.PackageUrl, metrics.BomRef, metrics.SerialNumber, diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 18c8c1d5..c248aab3 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -207,6 +207,7 @@ const trivySbomTable DBStatement = ` id UUID, cluster_name String, image_name String, + package_name String, package_url String, bom_ref String, serial_number String, @@ -229,6 +230,6 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index 9e68e610..647e34a4 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -13,6 +13,7 @@ type SbomData struct { ID string ClusterName string ComponentName string + PackageName string PackageUrl string BomRef string SerialNumber string diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index d717d65c..0e3a9c2e 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, cluster_name String, image_name String, + package_name String, package_url String, bom_ref String, serial_number String, From 166ee4eb4fed0f5071580f2a2b57f1518db40254 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 22 Dec 2023 18:26:18 +0530 Subject: [PATCH 175/263] log-removed --- agent/kubviz/trivy_sbom.go | 8 ++------ client/pkg/clickhouse/db_client.go | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index ff1b6bf5..5cc0f13c 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -20,14 +20,12 @@ func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) erro for _,packageinfo :=range report.Packages { for _, pkg := range packageinfo.Packages { - //log.Println(pkg.Name) - metrics := model.SbomData{ ID: uuid.New().String(), - ClusterName: ClusterName, + ClusterName: ClusterName, ComponentName: report.CycloneDX.Metadata.Component.Name, - PackageName: pkg.Name, + PackageName: pkg.Name, PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, BomRef: report.CycloneDX.Metadata.Component.BOMRef, SerialNumber: report.CycloneDX.SerialNumber, @@ -44,10 +42,8 @@ func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) erro return err } log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) - } } - return nil } diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 377fffe5..35460db3 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -714,8 +714,6 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { log.Fatal(err) } stmt.Close() - - log.Printf("**Clustername :%#v",metrics.ClusterName) } func (c *DBClient) Close() { _ = c.conn.Close() From c7a904defea40f9de839d536422d12de83a6191b Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 27 Dec 2023 13:30:27 +0530 Subject: [PATCH 176/263] Updated image tag --- charts/agent/Chart.yaml | 4 ++-- charts/agent/values.yaml | 6 +++--- charts/client/Chart.yaml | 4 ++-- charts/client/values.yaml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 67773fc7..70adb583 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.9 +version: 1.1.10 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.3" +appVersion: "v1.1.4" diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index f431427d..733927f0 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/kubviz-agent pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.3" + tag: "v1.1.4" imagePullSecrets: [] nameOverride: "" @@ -49,7 +49,7 @@ git_bridge: image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always - tag: "v1.1.3" + tag: "v1.1.4" resources: limits: cpu: 200m @@ -92,7 +92,7 @@ container_bridge: image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always - tag: "v1.1.3" + tag: "v1.1.4" resources: limits: cpu: 200m diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index b9231523..79c12d39 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.13 +version: 1.1.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.0.0" +appVersion: "v1.1.4" dependencies: - name: nats condition: nats.enabled diff --git a/charts/client/values.yaml b/charts/client/values.yaml index aa5f94b3..aa272cbc 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/client pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.3" + tag: "v1.1.4" imagePullSecrets: [] nameOverride: "" @@ -161,7 +161,7 @@ migration: image: repository: ghcr.io/intelops/kubviz/migration pullPolicy: Always - tag: "v1.1.3" + tag: "v1.1.4" schema: path: "/sql" From e42f85ecfa8bc25666f97b6a9ac4e2e83bcb5b1e Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Wed, 3 Jan 2024 20:22:37 +0530 Subject: [PATCH 177/263] increased version --- charts/client/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 79c12d39..bf6dfe4f 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.14 +version: 1.1.15 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to From 509a74238f7bca8968211fcd10ad6054166a57c4 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 9 Jan 2024 12:16:10 +0530 Subject: [PATCH 178/263] deleted unwanted file --- test.txt | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 test.txt diff --git a/test.txt b/test.txt deleted file mode 100644 index cf079bb9..00000000 --- a/test.txt +++ /dev/null @@ -1,28 +0,0 @@ -kubviz-tables : - -events - have datetime -trivy_vul - have datetime -trivysbom - have datetime -trivyimage - have datetime -trivy_misconfig - need to include time field in schema -rakkess - need to include time field in schema -DeprecatedAPIs - need to include time field in schema -DeletedAPIs - have datetime -getall_resources - need to include time field in schema -outdated_images - need to include time field in schema -kubescore - need to include time field in schema - -container-bridge tables: - -dockerhubbuild - need to include time field in schema -quaycontainerpush - need to include time field in schema -jfrogcontainerpush - need to include time field in schema -azurecontainerpush - time is in string - -git-bridge tables : - -azure_devops - time is in string -github - time is in string -gitlab - time is in string -bitbucket - time is in string -gitea - time is in string From 460c85e8cf749b6329c817cd546e53a19fccbb62 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 12 Jan 2024 12:17:23 +0530 Subject: [PATCH 179/263] opentelemetry --- agent/container/main.go | 13 ++ .../container/pkg/application/application.go | 10 ++ agent/container/pkg/application/handlers.go | 12 ++ agent/container/pkg/clients/nats_client.go | 11 ++ agent/container/pkg/handler/api_handler.go | 11 ++ .../container/pkg/handler/azure_container.go | 8 ++ .../pkg/handler/docker_event_dockerhub.go | 8 ++ .../container/pkg/handler/jfrog_container.go | 8 ++ agent/container/pkg/handler/quay_handler.go | 8 ++ agent/git/main.go | 12 ++ agent/git/pkg/application/application.go | 10 ++ agent/git/pkg/application/handlers.go | 32 +++++ agent/git/pkg/clients/nats_client.go | 11 ++ agent/kubviz/k8smetrics_agent.go | 35 ++++++ agent/kubviz/ketall.go | 10 ++ agent/kubviz/kubePreUpgrade.go | 10 ++ agent/kubviz/kube_score.go | 17 +++ agent/kubviz/outdated.go | 10 ++ agent/kubviz/rakees_agent.go | 10 ++ agent/kubviz/trivy.go | 18 +++ agent/kubviz/trivy_image.go | 10 ++ agent/kubviz/trivy_sbom.go | 18 +++ agent/server/server.go | 10 ++ client/main.go | 13 ++ client/pkg/application/application.go | 11 ++ client/pkg/clickhouse/db_client.go | 119 ++++++++++++++++++ client/pkg/clients/bridge_client.go | 11 ++ client/pkg/clients/container_client.go | 11 ++ client/pkg/clients/kubviz_client.go | 11 ++ go.mod | 36 +++--- go.sum | 77 +++++++----- pkg/opentelemetry/opentelemetry.go | 88 +++++++++++++ 32 files changed, 636 insertions(+), 43 deletions(-) create mode 100644 pkg/opentelemetry/opentelemetry.go diff --git a/agent/container/main.go b/agent/container/main.go index 3e684f3e..56b6958a 100755 --- a/agent/container/main.go +++ b/agent/container/main.go @@ -1,16 +1,29 @@ package main import ( + "context" "log" "os" "os/signal" "syscall" "github.com/intelops/kubviz/agent/container/pkg/application" + "github.com/intelops/kubviz/pkg/opentelemetry" ) func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) + + tp, err := opentelemetry.InitTracer() + if err != nil { + log.Fatal(err) + } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Printf("Error shutting down tracer provider: %v", err) + } + }() + app := application.New() go app.GithubContainerWatch() go app.Start() diff --git a/agent/container/pkg/application/application.go b/agent/container/pkg/application/application.go index 136fa332..7cabbbcd 100755 --- a/agent/container/pkg/application/application.go +++ b/agent/container/pkg/application/application.go @@ -11,7 +11,9 @@ import ( "github.com/intelops/kubviz/agent/container/pkg/clients" "github.com/intelops/kubviz/agent/container/pkg/config" "github.com/intelops/kubviz/agent/container/pkg/handler" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kelseyhightower/envconfig" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" ) type Application struct { @@ -43,6 +45,14 @@ func New() *Application { } r := gin.Default() + + config, err := opentelemetry.GetConfigurations() + if err != nil { + log.Println("Unable to read open telemetry configurations") + } + + r.Use(otelgin.Middleware(config.ServiceName)) + apiServer.BindRequest(r) httpServer := &http.Server{ diff --git a/agent/container/pkg/application/handlers.go b/agent/container/pkg/application/handlers.go index 28dfe829..cca3a95d 100755 --- a/agent/container/pkg/application/handlers.go +++ b/agent/container/pkg/application/handlers.go @@ -1,14 +1,26 @@ package application import ( + "context" "io" "log" "net/http" + + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) //githubHandler handles the github webhooks post requests. func (app *Application) localRegistryHandler(w http.ResponseWriter, r *http.Request) { + + ctx:=context.Background() + tracer := otel.Tracer("container-gitlab") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "localRegistryHandler") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + event, err := io.ReadAll(r.Body) if err != nil { log.Printf("Event body read failed: %v", err) diff --git a/agent/container/pkg/clients/nats_client.go b/agent/container/pkg/clients/nats_client.go index f42185cc..342fb2ea 100755 --- a/agent/container/pkg/clients/nats_client.go +++ b/agent/container/pkg/clients/nats_client.go @@ -1,11 +1,15 @@ package clients import ( + "context" "fmt" "log" "time" "github.com/intelops/kubviz/agent/container/pkg/config" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/nats-io/nats.go" ) @@ -112,6 +116,13 @@ func (n *NATSContext) Close() { // The repository information in the header can be used by subscribers to filter or route the event based on its origin or destination. // An error is returned if the publishing process fails, such as if the connection is lost or if there are issues with the JetStream. func (n *NATSContext) Publish(event []byte, repo string) error { + + ctx:=context.Background() + tracer := otel.Tracer("container-nats-client") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "ContainerPublish") + span.SetAttributes(attribute.String("repo-name", repo)) + defer span.End() + msg := nats.NewMsg(eventSubject) msg.Data = event msg.Header.Set("REPO_NAME", repo) diff --git a/agent/container/pkg/handler/api_handler.go b/agent/container/pkg/handler/api_handler.go index 531fd461..0e6bdd01 100755 --- a/agent/container/pkg/handler/api_handler.go +++ b/agent/container/pkg/handler/api_handler.go @@ -1,11 +1,14 @@ package handler import ( + "log" "net/http" "github.com/gin-gonic/gin" "github.com/intelops/kubviz/agent/container/api" "github.com/intelops/kubviz/agent/container/pkg/clients" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" ) type APIHandler struct { @@ -29,6 +32,14 @@ func NewAPIHandler(conn *clients.NATSContext) (*APIHandler, error) { } func (ah *APIHandler) BindRequest(r *gin.Engine) { + + config, err := opentelemetry.GetConfigurations() + if err != nil { + log.Println("Unable to read open telemetry configurations") + } + + r.Use(otelgin.Middleware(config.ServiceName)) + apiGroup := r.Group("/") { apiGroup.GET("/api-docs", ah.GetApiDocs) diff --git a/agent/container/pkg/handler/azure_container.go b/agent/container/pkg/handler/azure_container.go index 106d92e0..5e881703 100644 --- a/agent/container/pkg/handler/azure_container.go +++ b/agent/container/pkg/handler/azure_container.go @@ -9,6 +9,8 @@ import ( "github.com/gin-gonic/gin" "github.com/intelops/kubviz/model" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) var ErrInvalidPayload = errors.New("invalid or malformed Azure Container Registry webhook payload") @@ -19,6 +21,12 @@ var ErrInvalidPayload = errors.New("invalid or malformed Azure Container Registr // application to subscribe to these events and respond to changes in the container registry. // If the payload is invalid or the publishing process fails, an error response is returned. func (ah *APIHandler) PostEventAzureContainer(c *gin.Context) { + + tracer := otel.Tracer("azure-container") + _, span := tracer.Start(c.Request.Context(), "PostEventAzureContainer") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/container/pkg/handler/docker_event_dockerhub.go b/agent/container/pkg/handler/docker_event_dockerhub.go index d022d9a7..9066c947 100644 --- a/agent/container/pkg/handler/docker_event_dockerhub.go +++ b/agent/container/pkg/handler/docker_event_dockerhub.go @@ -7,6 +7,8 @@ import ( "net/http" "github.com/gin-gonic/gin" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) // parse errors @@ -16,6 +18,12 @@ var ( ) func (ah *APIHandler) PostEventDockerHub(c *gin.Context) { + + tracer := otel.Tracer("dockerhub-container") + _, span := tracer.Start(c.Request.Context(), "PostEventDockerHub") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/container/pkg/handler/jfrog_container.go b/agent/container/pkg/handler/jfrog_container.go index 5655026c..77b0451f 100644 --- a/agent/container/pkg/handler/jfrog_container.go +++ b/agent/container/pkg/handler/jfrog_container.go @@ -9,11 +9,19 @@ import ( "github.com/gin-gonic/gin" "github.com/intelops/kubviz/model" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) var ErrInvalidPayloads = errors.New("invalid or malformed jfrog Container Registry webhook payload") func (ah *APIHandler) PostEventJfrogContainer(c *gin.Context) { + + tracer := otel.Tracer("jfrog-container") + _, span := tracer.Start(c.Request.Context(), "PostEventJfrogContainer") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/container/pkg/handler/quay_handler.go b/agent/container/pkg/handler/quay_handler.go index a002e05d..b675658f 100644 --- a/agent/container/pkg/handler/quay_handler.go +++ b/agent/container/pkg/handler/quay_handler.go @@ -8,9 +8,17 @@ import ( "github.com/gin-gonic/gin" "github.com/intelops/kubviz/model" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) func (ah *APIHandler) PostEventQuayContainer(c *gin.Context) { + + tracer := otel.Tracer("quay-container") + _, span := tracer.Start(c.Request.Context(), "PostEventQuayContainer") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/git/main.go b/agent/git/main.go index 9fd6a682..c1d22c27 100644 --- a/agent/git/main.go +++ b/agent/git/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "log" "os" "os/signal" @@ -9,6 +10,7 @@ import ( "github.com/intelops/kubviz/agent/git/pkg/application" "github.com/intelops/kubviz/agent/git/pkg/clients" "github.com/intelops/kubviz/agent/git/pkg/config" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kelseyhightower/envconfig" ) @@ -20,6 +22,16 @@ func main() { log.Fatalf("Could not parse env Config: %v", err) } + tp, err := opentelemetry.InitTracer() + if err != nil { + log.Fatal(err) + } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Printf("Error shutting down tracer provider: %v", err) + } + }() + // Connect to NATS natsContext, err := clients.NewNATSContext(cfg) if err != nil { diff --git a/agent/git/pkg/application/application.go b/agent/git/pkg/application/application.go index 8af7ba95..0ea3b17e 100644 --- a/agent/git/pkg/application/application.go +++ b/agent/git/pkg/application/application.go @@ -11,6 +11,8 @@ import ( "github.com/intelops/kubviz/agent/git/api" "github.com/intelops/kubviz/agent/git/pkg/clients" "github.com/intelops/kubviz/agent/git/pkg/config" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "github.com/gin-gonic/gin" ) @@ -41,6 +43,14 @@ func New(conf *config.Config, conn *clients.NATSContext) *Application { func (app *Application) Routes() *gin.Engine { router := gin.New() + + config, err := opentelemetry.GetConfigurations() + if err != nil { + log.Println("Unable to read open telemetry configurations") + } + + router.Use(otelgin.Middleware(config.ServiceName)) + api.RegisterHandlers(router, app) return router } diff --git a/agent/git/pkg/application/handlers.go b/agent/git/pkg/application/handlers.go index b865c380..cb2c7cfe 100644 --- a/agent/git/pkg/application/handlers.go +++ b/agent/git/pkg/application/handlers.go @@ -9,10 +9,18 @@ import ( "github.com/intelops/kubviz/agent/git/api" "github.com/intelops/kubviz/gitmodels/azuremodel" "github.com/intelops/kubviz/model" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) func (app *Application) PostGitea(c *gin.Context) { log.Println("gitea handler called...") + + tracer := otel.Tracer("gitea-git") + _, span := tracer.Start(c.Request.Context(), "PostGitea") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer log.Println("gitea handler exited...") event := c.Request.Header.Get(string(model.GiteaHeader)) @@ -30,6 +38,12 @@ func (app *Application) PostGitea(c *gin.Context) { func (app *Application) PostAzure(c *gin.Context) { log.Println("azure handler called...") + + tracer := otel.Tracer("azure-git") + _, span := tracer.Start(c.Request.Context(), "PostAzure") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer log.Println("azure handler exited...") jsonData, err := c.GetRawData() @@ -58,6 +72,12 @@ func (app *Application) PostAzure(c *gin.Context) { // githubHandler handles the github webhooks post requests. func (app *Application) PostGithub(c *gin.Context) { log.Println("github handler called...") + + tracer := otel.Tracer("github-git") + _, span := tracer.Start(c.Request.Context(), "PostGithub") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer log.Println("github handler exited...") event := c.Request.Header.Get(string(model.GithubHeader)) @@ -79,6 +99,12 @@ func (app *Application) PostGithub(c *gin.Context) { // gitlabHandler handles the github webhooks post requests. func (app *Application) PostGitlab(c *gin.Context) { log.Println("gitlab handler called...") + + tracer := otel.Tracer("gitlab-git") + _, span := tracer.Start(c.Request.Context(), "PostGitlab") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer log.Println("gitlab handler exited...") event := c.Request.Header.Get(string(model.GitlabHeader)) @@ -100,6 +126,12 @@ func (app *Application) PostGitlab(c *gin.Context) { // bitBucketHandler handles the github webhooks post requests. func (app *Application) PostBitbucket(c *gin.Context) { log.Println("bitbucket handler called...") + + tracer := otel.Tracer("bitbucket-git") + _, span := tracer.Start(c.Request.Context(), "PostBitbucket") + span.SetAttributes(attribute.String("http.method", "POST")) + defer span.End() + defer log.Println("bitbucket handler exited...") event := c.Request.Header.Get(string(model.BitBucketHeader)) diff --git a/agent/git/pkg/clients/nats_client.go b/agent/git/pkg/clients/nats_client.go index e9ef06c2..b42f9dcf 100644 --- a/agent/git/pkg/clients/nats_client.go +++ b/agent/git/pkg/clients/nats_client.go @@ -1,10 +1,14 @@ package clients import ( + "context" "fmt" "github.com/intelops/kubviz/agent/git/pkg/config" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "log" "time" @@ -91,6 +95,13 @@ func (n *NATSContext) Close() { } func (n *NATSContext) Publish(metric []byte, repo string, eventkey model.EventKey, eventvalue model.EventValue) error { + + ctx:=context.Background() + tracer := otel.Tracer("git-nats-client") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "GitPublish") + span.SetAttributes(attribute.String("repo-name", repo)) + defer span.End() + msg := nats.NewMsg(eventSubject) msg.Data = metric msg.Header.Set("GitProvider", repo) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index fbc9b24c..2dd8f127 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -11,6 +11,8 @@ import ( "time" "github.com/intelops/go-common/logging" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/go-co-op/gocron" "github.com/nats-io/nats.go" @@ -19,6 +21,7 @@ import ( "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -98,6 +101,17 @@ func main() { } clientset = getK8sClient(config) } + + tp, err := opentelemetry.InitTracer() + if err != nil { + log.Fatal(err) + } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Printf("Error shutting down tracer provider: %v", err) + } + }() + go publishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { @@ -151,11 +165,25 @@ func main() { // publishMetrics publishes stream of events // with subject "METRICS.created" func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { + + ctx:=context.Background() + tracer := otel.Tracer("kubviz-publish-metrics") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishMetrics") + span.SetAttributes(attribute.String("kubviz-agent", "publish-metrics")) + defer span.End() + watchK8sEvents(clientset, js) errCh <- nil } func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext) (bool, error) { + + ctx:=context.Background() + tracer := otel.Tracer("kubviz-publish-k8smetrics") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishK8sMetrics") + span.SetAttributes(attribute.String("kubviz-agent", "publish-k8smetrics")) + defer span.End() + metrics := model.Metrics{ ID: id, Type: mtype, @@ -250,6 +278,13 @@ func LogErr(err error) { } } func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { + + ctx:=context.Background() + tracer := otel.Tracer("kubviz-watch-k8sevents") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "watchK8sEvents") + span.SetAttributes(attribute.String("kubviz-agent", "watch-k8sevents")) + defer span.End() + watchlist := cache.NewListWatchFromClient( clientset.CoreV1().RESTClient(), "events", diff --git a/agent/kubviz/ketall.go b/agent/kubviz/ketall.go index ce6d125f..ffc2e7b4 100644 --- a/agent/kubviz/ketall.go +++ b/agent/kubviz/ketall.go @@ -6,6 +6,9 @@ import ( "time" "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" @@ -29,6 +32,13 @@ func PublishAllResources(result model.Resource, js nats.JetStreamContext) error } func GetAllResources(config *rest.Config, js nats.JetStreamContext) error { + + ctx:=context.Background() + tracer := otel.Tracer("ketall") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "GetAllResources") + span.SetAttributes(attribute.String("ketall-plugin-agent", "ketall-output")) + defer span.End() + // TODO: upto this uncomment for production // Create a new discovery client to discover all resources in the cluster dc := discovery.NewDiscoveryClientForConfigOrDie(config) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/kubePreUpgrade.go index 18c1597a..68577ac0 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/kubePreUpgrade.go @@ -10,6 +10,9 @@ import ( "strings" "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -79,6 +82,13 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon } func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error { + + ctx := context.Background() + tracer := otel.Tracer("kubepreupgrade") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "KubePreUpgradeDetector") + span.SetAttributes(attribute.String("kubepug-plugin-agent", "kubepug-output")) + defer span.End() + pvcMountPath := "/mnt/agent/kbz" uniqueDir := fmt.Sprintf("%s/kubepug", pvcMountPath) err := os.MkdirAll(uniqueDir, 0755) diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/kube_score.go index 57ea8cb8..90551e0f 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/kube_score.go @@ -9,8 +9,11 @@ import ( "github.com/google/uuid" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" "github.com/zegl/kube-score/renderer/json_v2" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -57,6 +60,13 @@ func publish(ns string, js nats.JetStreamContext) error { } func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamContext) error { + + ctx:=context.Background() + tracer := otel.Tracer("kubescore") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishKubescoreMetrics") + span.SetAttributes(attribute.String("kubescore-plugin-agent", "kubescore-output")) + defer span.End() + metrics := model.KubeScoreRecommendations{ ID: uuid.New().String(), ClusterName: ClusterName, @@ -73,6 +83,13 @@ func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamCon } func executeCommand(command string) (string, error) { + + ctx:=context.Background() + tracer := otel.Tracer("kubescore") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommand") + span.SetAttributes(attribute.String("kubescore-agent", "kubescore-command-running")) + defer span.End() + cmd := exec.Command("/bin/sh", "-c", command) stdout, err := cmd.Output() diff --git a/agent/kubviz/outdated.go b/agent/kubviz/outdated.go index ce3a77d3..8b2fe748 100644 --- a/agent/kubviz/outdated.go +++ b/agent/kubviz/outdated.go @@ -15,6 +15,9 @@ import ( "time" "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" @@ -56,6 +59,13 @@ func truncateTagName(tagName string) string { return truncatedTagName } func PublishOutdatedImages(out model.CheckResultfinal, js nats.JetStreamContext) error { + + ctx:=context.Background() + tracer := otel.Tracer("outdated-images") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishOutdatedImages") + span.SetAttributes(attribute.String("outdated-plugin-agent", "outdated-output")) + defer span.End() + metrics := out metrics.ClusterName = ClusterName metricsJson, _ := json.Marshal(metrics) diff --git a/agent/kubviz/rakees_agent.go b/agent/kubviz/rakees_agent.go index 0b8426c1..0b1d142c 100644 --- a/agent/kubviz/rakees_agent.go +++ b/agent/kubviz/rakees_agent.go @@ -10,6 +10,9 @@ import ( "syscall" "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/intelops/kubviz/agent/kubviz/rakkess" "github.com/intelops/kubviz/model" @@ -34,6 +37,13 @@ func accessToOutcome(access rakkess.Access) (rakkess.Outcome, error) { } func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { + + ctx:=context.Background() + tracer := otel.Tracer("rakees") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RakeesOutput") + span.SetAttributes(attribute.String("rakees-plugin-agent", "rakees-output")) + defer span.End() + // Create a new Kubernetes client client, err := kubernetes.NewForConfig(config) if err != nil { diff --git a/agent/kubviz/trivy.go b/agent/kubviz/trivy.go index 848f2595..2cedf804 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/trivy.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/json" "fmt" "log" @@ -13,10 +14,20 @@ import ( "github.com/google/uuid" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) func executeCommandTrivy(command string) ([]byte, error) { + + ctx := context.Background() + tracer := otel.Tracer("trivy-cluster") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandTrivy") + span.SetAttributes(attribute.String("trivy-k8s-agent", "command-running")) + defer span.End() + cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc @@ -39,6 +50,13 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { return err } var report report.ConsolidatedReport + + ctx := context.Background() + tracer := otel.Tracer("trivy-cluster") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivyK8sClusterScan") + span.SetAttributes(attribute.String("cluster-name", report.ClusterName)) + defer span.End() + cmdString := fmt.Sprintf("trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir %s --debug", trivyCacheDir) // clearCacheCmd := "trivy k8s --clear-cache" out, err := executeCommandTrivy(cmdString) diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/trivy_image.go index 8a64fb5c..d8d70e5a 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/trivy_image.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/json" "fmt" "log" @@ -11,7 +12,10 @@ import ( "github.com/google/uuid" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "k8s.io/client-go/rest" ) @@ -25,6 +29,12 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { } // clearCacheCmd := "trivy image --clear-cache" + ctx:=context.Background() + tracer := otel.Tracer("trivy-image") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivyImageScans") + span.SetAttributes(attribute.String("trivy-image-scan-agent", "image-scan")) + defer span.End() + images, err := ListImages(config) if err != nil { log.Println("error occured while trying to list images, error :", err.Error()) diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/trivy_sbom.go index 5cc0f13c..944037db 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/trivy_sbom.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/json" "fmt" "log" @@ -12,7 +13,10 @@ import ( "github.com/google/uuid" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "k8s.io/client-go/rest" ) @@ -48,6 +52,13 @@ func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) erro } func executeCommandSbom(command string) ([]byte, error) { + + ctx:=context.Background() + tracer := otel.Tracer("trivy-sbom") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandSbom") + span.SetAttributes(attribute.String("trivy-sbom-agent", "sbom-command-running")) + defer span.End() + cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc @@ -68,6 +79,13 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error creating Trivy cache directory: %v\n", err) return err } + + ctx:=context.Background() + tracer := otel.Tracer("trivy-sbom") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivySbomScan") + span.SetAttributes(attribute.String("sbom", "sbom-creation")) + defer span.End() + images, err := ListImages(config) if err != nil { diff --git a/agent/server/server.go b/agent/server/server.go index cee938ae..70eb3b3e 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -6,6 +6,8 @@ import ( _ "net/http/pprof" "github.com/gin-gonic/gin" + "github.com/intelops/kubviz/pkg/opentelemetry" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" ) func EnableProfile(r *gin.Engine) { @@ -32,6 +34,14 @@ func EnableProfile(r *gin.Engine) { func StartServer() { r := gin.Default() + + config, err := opentelemetry.GetConfigurations() + if err != nil { + log.Println("Unable to read open telemetry configurations") + } + + r.Use(otelgin.Middleware(config.ServiceName)) + EnableProfile(r) log.Fatal(r.Run(":8080")) } diff --git a/client/main.go b/client/main.go index e3c5611c..af2316db 100644 --- a/client/main.go +++ b/client/main.go @@ -1,17 +1,30 @@ package main import ( + "context" "log" "os" "os/signal" "syscall" "github.com/intelops/kubviz/client/pkg/application" + "github.com/intelops/kubviz/pkg/opentelemetry" ) func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) log.Println("new client running...") + + tp, err := opentelemetry.InitTracer() + if err != nil { + log.Fatal(err) + } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Printf("Error shutting down tracer provider: %v", err) + } + }() + app := application.Start() signals := make(chan os.Signal, 1) diff --git a/client/pkg/application/application.go b/client/pkg/application/application.go index d95e8ede..c7bfd8eb 100644 --- a/client/pkg/application/application.go +++ b/client/pkg/application/application.go @@ -1,12 +1,16 @@ package application import ( + "context" "log" "github.com/intelops/kubviz/client/pkg/clickhouse" "github.com/intelops/kubviz/client/pkg/clients" "github.com/intelops/kubviz/client/pkg/config" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kelseyhightower/envconfig" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) type Application struct { @@ -17,6 +21,13 @@ type Application struct { func Start() *Application { log.Println("Client Application started...") + + ctx:=context.Background() + tracer := otel.Tracer("kubviz-client") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "Start") + span.SetAttributes(attribute.String("start-app-client", "application")) + defer span.End() + cfg := &config.Config{} if err := envconfig.Process("", cfg); err != nil { log.Fatalf("Could not parse env Config: %v", err) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 35460db3..ff17af95 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -10,12 +10,15 @@ import ( "time" "github.com/ClickHouse/clickhouse-go/v2" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" "github.com/intelops/kubviz/client/pkg/config" "github.com/intelops/kubviz/gitmodels/dbstatement" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" ) type DBClient struct { @@ -134,6 +137,12 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushEventPayload) { + ctx:=context.Background() + tracer := otel.Tracer("insert-container-azure") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventAzure") + span.SetAttributes(attribute.String("container-azure-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -183,6 +192,13 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE } func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-container-quay") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventQuay") + span.SetAttributes(attribute.String("container-quay-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -234,6 +250,13 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload } func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-container-jfrog") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventJfrog") + span.SetAttributes(attribute.String("container-jfrog-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -285,6 +308,13 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE } func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-rakees-metrics") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertRakeesMetrics") + span.SetAttributes(attribute.String("rakees-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -315,6 +345,13 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { } func (c *DBClient) InsertKetallEvent(metrics model.Resource) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-ketall-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKetallEvent") + span.SetAttributes(attribute.String("ketall-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -344,6 +381,13 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { } func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-outdated-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertOutdatedEvent") + span.SetAttributes(attribute.String("outdated-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -375,6 +419,13 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { } func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-depricated-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertDeprecatedAPI") + span.SetAttributes(attribute.String("depricated-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -413,6 +464,13 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { } func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-deletedapi") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertDeletedAPI") + span.SetAttributes(attribute.String("deletedapi-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -452,6 +510,13 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { } func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { + + ctx:=context.Background() + tracer := otel.Tracer("insert-kubviz-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKubvizEvent") + span.SetAttributes(attribute.String("kubvizevent-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -489,6 +554,12 @@ func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { } func (c *DBClient) InsertGitEvent(event string) { ctx := context.Background() + + tracer := otel.Tracer("insert-git-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertGitEvent") + span.SetAttributes(attribute.String("git-client", "insert")) + defer span.End() + batch, err := c.splconn.PrepareBatch(ctx, "INSERT INTO git_json") if err != nil { log.Fatal(err) @@ -504,6 +575,12 @@ func (c *DBClient) InsertGitEvent(event string) { } func (c *DBClient) InsertContainerEvent(event string) { ctx := context.Background() + + tracer := otel.Tracer("insert-container-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEvent") + span.SetAttributes(attribute.String("container-client", "insert")) + defer span.End() + batch, err := c.splconn.PrepareBatch(ctx, "INSERT INTO container_bridge") if err != nil { log.Fatal(err) @@ -519,6 +596,14 @@ func (c *DBClient) InsertContainerEvent(event string) { } func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations) { + + ctx := context.Background() + + tracer := otel.Tracer("insert-kubescore-event") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKubeScoreMetrics") + span.SetAttributes(attribute.String("kubescore-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -564,6 +649,13 @@ func (c *DBClient) InsertKubeScoreMetrics(metrics model.KubeScoreRecommendations } func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { + + ctx := context.Background() + tracer := otel.Tracer("insert-trivy-metrics") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertTrivyMetrics") + span.SetAttributes(attribute.String("trivy-metrics-client", "insert")) + defer span.End() + for _, finding := range metrics.Report.Findings { for _, result := range finding.Results { for _, vulnerability := range result.Vulnerabilities { @@ -644,6 +736,13 @@ func (c *DBClient) InsertTrivyMetrics(metrics model.Trivy) { } func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { + + ctx := context.Background() + tracer := otel.Tracer("insert-trivy-image") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertTrivyImageMetrics") + span.SetAttributes(attribute.String("trivy-image-client", "insert")) + defer span.End() + for _, result := range metrics.Report.Results { for _, vulnerability := range result.Vulnerabilities { tx, err := c.conn.Begin() @@ -688,6 +787,12 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { log.Println("####started inserting value") + ctx := context.Background() + tracer := otel.Tracer("insert-trivy-sbom") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertTrivySbomMetrics") + span.SetAttributes(attribute.String("trivy-sbom-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -815,6 +920,13 @@ func (c *DBClient) RetrieveKubvizEvent() ([]model.DbEvent, error) { } func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { + + ctx := context.Background() + tracer := otel.Tracer("insert-container-dockerhub") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventDockerHub") + span.SetAttributes(attribute.String("container-dockerhub-client", "insert")) + defer span.End() + tx, err := c.conn.Begin() if err != nil { log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) @@ -845,6 +957,13 @@ func (c *DBClient) InsertContainerEventDockerHub(build model.DockerHubBuild) { } func (c *DBClient) InsertContainerEventGithub(event string) { + + ctx := context.Background() + tracer := otel.Tracer("insert-container-github") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventGithub") + span.SetAttributes(attribute.String("container-github-client", "insert")) + defer span.End() + var image model.GithubImage err := json.Unmarshal([]byte(event), &image) if err != nil { diff --git a/client/pkg/clients/bridge_client.go b/client/pkg/clients/bridge_client.go index 3082bf57..999cc07a 100644 --- a/client/pkg/clients/bridge_client.go +++ b/client/pkg/clients/bridge_client.go @@ -1,6 +1,7 @@ package clients import ( + "context" "encoding/json" "errors" "log" @@ -15,7 +16,10 @@ import ( "github.com/intelops/kubviz/gitmodels/azuremodel" "github.com/intelops/kubviz/gitmodels/dbstatement" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) // ErrHeaderEmpty defines an error occur when header is empty in git stream @@ -36,6 +40,13 @@ const ( // the respective funcs to insert data into clickhouse DB func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { log.Printf("Creating nats consumer %s with subject: %s \n", bridgeConsumer, bridgeSubject) + + ctx:=context.Background() + tracer := otel.Tracer("git-client") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "SubscribeGitBridgeNats") + span.SetAttributes(attribute.String("git-subscribe", "Subscribe")) + defer span.End() + n.stream.Subscribe(string(bridgeSubject), func(msg *nats.Msg) { msg.Ack() gitprovider := msg.Header.Get("GitProvider") diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index 29dc5763..cea17181 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -1,6 +1,7 @@ package clients import ( + "context" "encoding/json" "errors" "log" @@ -8,7 +9,10 @@ import ( "github.com/intelops/kubviz/client/pkg/clickhouse" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) var ( @@ -26,6 +30,13 @@ const ( ) func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { + + ctx:=context.Background() + tracer := otel.Tracer("container-client") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "SubscribeContainerNats") + span.SetAttributes(attribute.String("container-subscribe", "Subscribe")) + defer span.End() + n.stream.Subscribe(string(containerSubject), func(msg *nats.Msg) { msg.Ack() repoName := msg.Header.Get("REPO_NAME") diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index 683cd563..d4854dfa 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -1,11 +1,15 @@ package clients import ( + "context" "encoding/json" "log" "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "github.com/intelops/kubviz/client/pkg/clickhouse" "github.com/intelops/kubviz/model" @@ -18,6 +22,13 @@ type SubscriptionInfo struct { } func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { + + ctx:=context.Background() + tracer := otel.Tracer("kubviz-client") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "SubscribeAllKubvizNats") + span.SetAttributes(attribute.String("kubviz-subscribe", "subscribe")) + defer span.End() + subscribe := func(sub SubscriptionInfo) { n.stream.Subscribe(sub.Subject, sub.Handler, nats.Durable(sub.Consumer), nats.ManualAck()) } diff --git a/go.mod b/go.mod index 48f274bd..e6b1f544 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/go-co-op/gocron v1.30.1 github.com/go-playground/webhooks/v6 v6.2.0 github.com/golang-migrate/migrate/v4 v4.16.2 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.3.1 github.com/hashicorp/go-version v1.6.0 github.com/intelops/go-common v1.0.19 github.com/kelseyhightower/envconfig v1.4.0 @@ -25,7 +25,12 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/zegl/kube-score v1.17.0 - golang.org/x/term v0.11.0 + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 + go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 + go.opentelemetry.io/otel/sdk v1.21.0 + golang.org/x/term v0.14.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 k8s.io/cli-runtime v0.27.3 @@ -34,7 +39,7 @@ require ( ) require ( - cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go v0.110.7 // indirect cloud.google.com/go/iam v1.1.1 // indirect cloud.google.com/go/storage v1.30.1 // indirect github.com/ClickHouse/ch-go v0.52.1 // indirect @@ -47,6 +52,7 @@ require ( github.com/aquasecurity/trivy-db v0.0.0-20230703082116-dc52e83376ce // indirect github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230628140707-dae3bdb6ee81 // indirect github.com/bytedance/sonic v1.9.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -60,7 +66,8 @@ require ( github.com/go-errors/errors v1.4.2 // indirect github.com/go-faster/city v1.0.1 // indirect github.com/go-faster/errors v0.6.1 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -72,12 +79,13 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-containerregistry v0.15.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/imdario/mergo v0.3.15 // indirect @@ -88,7 +96,6 @@ require ( github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -127,26 +134,27 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.etcd.io/bbolt v1.3.7 // indirect - go.opentelemetry.io/otel v1.14.0 // indirect - go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.15.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.126.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index ce5d958d..ce8b6444 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= -cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= @@ -69,6 +69,8 @@ github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4 github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= @@ -153,9 +155,11 @@ github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8 github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= github.com/go-gorp/gorp/v3 v3.0.5 h1:PUjzYdYu3HBOh8LE+UUmRG2P0IRDak9XMeGNvaeq4Ow= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -187,6 +191,7 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA= github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -213,8 +218,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -225,8 +231,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= @@ -237,6 +243,8 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.11.1/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -286,7 +294,6 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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= @@ -400,7 +407,6 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rubenv/sql-migrate v1.3.1 h1:Vx+n4Du8X8VTYuXbhNxdEUoh6wiJERA0GlWocR5FrbA= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= @@ -478,17 +484,29 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= -go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= -go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= -go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 h1:mMv2jG58h6ZI5t5S9QCVGdzCmAsTakMa3oxVgpSD44g= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1/go.mod h1:oqRuNKG0upTaDPbLVCG8AD0G2ETrfDtmh7jViy7ox6M= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1 h1:WPYiUgmw3+b7b3sQ1bFBFAf0q+Di9dvNc3AtYfnT4RQ= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -500,8 +518,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -522,8 +540,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= @@ -550,18 +568,18 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -591,16 +609,17 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb h1:Isk1sSH7bovx8Rti2wZK0UZF6oraBDK74uoyLEEVFN0= google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/opentelemetry/opentelemetry.go b/pkg/opentelemetry/opentelemetry.go new file mode 100644 index 00000000..f3c6d2dd --- /dev/null +++ b/pkg/opentelemetry/opentelemetry.go @@ -0,0 +1,88 @@ +package opentelemetry + +import ( + "context" + "log" + + "github.com/kelseyhightower/envconfig" + "github.com/pkg/errors" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +type Configurations struct { + ServiceName string `envconfig:"APPLICATION_NAME" default:"Kubviz"` + CollectorURL string `envconfig:"OPTEL_URL" default:"otelcollector.azureagent.optimizor.app:80"` +} + +func GetConfigurations() (opteConfig *Configurations, err error) { + opteConfig = &Configurations{} + if err = envconfig.Process("", opteConfig); err != nil { + return nil, errors.WithStack(err) + } + return +} + +func InitTracer() (*sdktrace.TracerProvider, error) { + ctx := context.Background() + + config, err := GetConfigurations() + if err != nil { + log.Println("Unable to read open telemetry configurations") + return nil, err + } + + headers := map[string]string{ + "signoz-service-name": config.ServiceName, + } + + client := otlptracegrpc.NewClient( + otlptracegrpc.WithEndpoint(config.CollectorURL), + otlptracegrpc.WithHeaders(headers), + otlptracegrpc.WithInsecure(), + ) + + exporter, err := otlptrace.New(ctx, client) + if err != nil { + log.Fatalf("failed to initialize exporter: %e", err) + } + + res, err := resource.New( + ctx, + resource.WithAttributes( + attribute.String("service.name", config.ServiceName), + attribute.String("library.language", "go"), + + ), + ) + if err != nil { + log.Fatalf("failed to initialize resource: %e", err) + } + + // Create the trace provider + tp := sdktrace.NewTracerProvider( + trace.WithSampler(trace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource(res), + ) + + // Set the global trace provider + otel.SetTracerProvider(tp) + + // Set the propagator + propagator := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}) + otel.SetTextMapPropagator(propagator) + + return tp, nil +} + +func BuildContext(ctx context.Context) context.Context { + newCtx, _ := context.WithCancel(ctx) + return newCtx +} \ No newline at end of file From b60c5428b2748a64485e5733cffc32189286ba5a Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 16 Jan 2024 17:37:02 +0530 Subject: [PATCH 180/263] added graphql server --- client/pkg/application/application.go | 2 +- client/pkg/clickhouse/db_client.go | 60 +- dockerfiles/graphqlserver/Dockerfile | 12 + go.mod | 17 +- go.sum | 43 +- graphqlserver/gqlgen.yml | 87 + graphqlserver/graph/generated.go | 5414 +++++++++++++++++++++++ graphqlserver/graph/model/models_gen.go | 50 + graphqlserver/graph/resolver.go | 31 + graphqlserver/graph/schema.graphqls | 47 + graphqlserver/graph/schema.resolvers.go | 158 + graphqlserver/server.go | 28 + steps-to-test.txt | 45 +- 13 files changed, 5944 insertions(+), 50 deletions(-) create mode 100644 dockerfiles/graphqlserver/Dockerfile create mode 100644 graphqlserver/gqlgen.yml create mode 100644 graphqlserver/graph/generated.go create mode 100644 graphqlserver/graph/model/models_gen.go create mode 100644 graphqlserver/graph/resolver.go create mode 100644 graphqlserver/graph/schema.graphqls create mode 100644 graphqlserver/graph/schema.resolvers.go create mode 100644 graphqlserver/server.go diff --git a/client/pkg/application/application.go b/client/pkg/application/application.go index d95e8ede..972204a9 100644 --- a/client/pkg/application/application.go +++ b/client/pkg/application/application.go @@ -21,7 +21,7 @@ func Start() *Application { if err := envconfig.Process("", cfg); err != nil { log.Fatalf("Could not parse env Config: %v", err) } - dbClient, err := clickhouse.NewDBClient(cfg) + dbClient, _, err := clickhouse.NewDBClient(cfg) if err != nil { log.Fatal(err) } diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 35460db3..f5fa391f 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -48,7 +48,7 @@ type DBInterface interface { Close() } -func NewDBClient(conf *config.Config) (DBInterface, error) { +func NewDBClient(conf *config.Config) (DBInterface, *sql.DB, error) { ctx := context.Background() var connOptions clickhouse.Options @@ -87,7 +87,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { splconn, err := clickhouse.Open(&connOptions) if err != nil { - return nil, err + return nil, nil, err } if err := splconn.Ping(ctx); err != nil { @@ -96,7 +96,7 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { } else { fmt.Println("Authentication error:", err) // Print the error message here } - return nil, err + return nil, nil, err } var connOption clickhouse.Options @@ -126,10 +126,10 @@ func NewDBClient(conf *config.Config) (DBInterface, error) { } else { fmt.Println("Authentication error:", err) } - return nil, err + return nil, nil, err } - return &DBClient{splconn: splconn, conn: stdconn, conf: conf}, nil + return &DBClient{splconn: splconn, conn: stdconn, conf: conf}, stdconn, nil } func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushEventPayload) { @@ -688,32 +688,32 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { log.Println("####started inserting value") - tx, err := c.conn.Begin() - if err != nil { - log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) - } - stmt, err := tx.Prepare(InsertTrivySbom) - if err != nil { - log.Fatalf("error preparing statement: %v", err) - } + tx, err := c.conn.Begin() + if err != nil { + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) + } + stmt, err := tx.Prepare(InsertTrivySbom) + if err != nil { + log.Fatalf("error preparing statement: %v", err) + } - if _, err := stmt.Exec( - metrics.ID, - metrics.ClusterName, - metrics.ComponentName, - metrics.PackageName, - metrics.PackageUrl, - metrics.BomRef, - metrics.SerialNumber, - int32(metrics.CycloneDxVersion), - metrics.BomFormat, - ); err != nil { - log.Fatal(err) - } - if err := tx.Commit(); err != nil { - log.Fatal(err) - } - stmt.Close() + if _, err := stmt.Exec( + metrics.ID, + metrics.ClusterName, + metrics.ComponentName, + metrics.PackageName, + metrics.PackageUrl, + metrics.BomRef, + metrics.SerialNumber, + int32(metrics.CycloneDxVersion), + metrics.BomFormat, + ); err != nil { + log.Fatal(err) + } + if err := tx.Commit(); err != nil { + log.Fatal(err) + } + stmt.Close() } func (c *DBClient) Close() { _ = c.conn.Close() diff --git a/dockerfiles/graphqlserver/Dockerfile b/dockerfiles/graphqlserver/Dockerfile new file mode 100644 index 00000000..8b0e8111 --- /dev/null +++ b/dockerfiles/graphqlserver/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.19 AS builder +WORKDIR / +COPY ./ ./ + +RUN go mod download +RUN CGO_ENABLED=0 go build -o ./build/graphqlserver graphqlserver/server.go + +FROM scratch +COPY --from=builder ./build/graphqlserver server + +USER 65532:65532 +ENTRYPOINT ["/server"] diff --git a/go.mod b/go.mod index 48f274bd..0ba34eea 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/intelops/kubviz go 1.20 require ( + github.com/99designs/gqlgen v0.17.42 github.com/ClickHouse/clickhouse-go/v2 v2.10.1 github.com/aquasecurity/trivy v0.43.1 github.com/blang/semver v3.5.1+incompatible @@ -24,8 +25,9 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 + github.com/vektah/gqlparser/v2 v2.5.10 github.com/zegl/kube-score v1.17.0 - golang.org/x/term v0.11.0 + golang.org/x/term v0.13.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 k8s.io/cli-runtime v0.27.3 @@ -39,6 +41,7 @@ require ( cloud.google.com/go/storage v1.30.1 // indirect github.com/ClickHouse/ch-go v0.52.1 // indirect github.com/CycloneDX/cyclonedx-go v0.7.2-0.20230625092137-07e2f29defc3 // indirect + github.com/agnivade/levenshtein v1.1.1 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/aquasecurity/go-dep-parser v0.0.0-20230626110909-e7ea5097483b // indirect @@ -77,9 +80,11 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.1.0 // indirect @@ -96,6 +101,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect @@ -119,6 +125,7 @@ require ( github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/showa-93/go-mask v0.6.0 // indirect + github.com/sosodev/duration v1.1.0 // indirect github.com/spdx/tools-golang v0.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect @@ -135,12 +142,12 @@ require ( go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.126.0 // indirect diff --git a/go.sum b/go.sum index ce5d958d..a157915c 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +github.com/99designs/gqlgen v0.17.42 h1:BVWDOb2VVHQC5k3m6oa0XhDnxltLLrU4so7x/u39Zu4= +github.com/99designs/gqlgen v0.17.42/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -30,14 +32,18 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= +github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= +github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM= @@ -54,6 +60,8 @@ github.com/aquasecurity/trivy-db v0.0.0-20230703082116-dc52e83376ce h1:swoQLWQoZ github.com/aquasecurity/trivy-db v0.0.0-20230703082116-dc52e83376ce/go.mod h1:cXuqKo+FaMY0ixJNoUcyDHdfCBRPWOysI2Td8N4fRsg= github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230628140707-dae3bdb6ee81 h1:5/tKpCr861auON/CMHSXnRzNixx1FTWAeHSwV0PtA0U= github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230628140707-dae3bdb6ee81/go.mod h1:GCm7uq++jz7Ij8cA9mAorpKJ9/qSBCl7v6EKYA8DxJ8= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/aws/aws-sdk-go v1.44.245 h1:KtY2s4q31/kn33AdV63R5t77mdxsI7rq3YT7Mgo805M= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= @@ -95,6 +103,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/docker/cli v0.0.0-20190913211141-95327f4e6241/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= @@ -233,6 +243,8 @@ github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5i github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -248,6 +260,8 @@ github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhE github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= +github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= @@ -323,6 +337,7 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/moby/buildkit v0.11.6 h1:VYNdoKk5TVxN7k4RvZgdeM4GOyRvIi4Z8MXOY7xvyUs= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -410,7 +425,7 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/showa-93/go-mask v0.6.0 h1:nNW3dgEocYB7QCGzgRx9wlYrepEg+tRw/keg7u1ftY8= @@ -421,6 +436,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= +github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4= +github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/spdx/tools-golang v0.5.0 h1:/fqihV2Jna7fmow65dHpgKNsilgLK7ICpd2tkCnPEyY= github.com/spdx/tools-golang v0.5.0/go.mod h1:kkGlrSXXfHwuSzHQZJRV3aKu9ZXCq/MSf2+xyiJH1lM= @@ -457,6 +474,8 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95 github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/vektah/gqlparser/v2 v2.5.10 h1:6zSM4azXC9u4Nxy5YmdmGu4uKamfwsdKTwp5zsEealU= +github.com/vektah/gqlparser/v2 v2.5.10/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= @@ -500,8 +519,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -522,8 +541,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= @@ -550,18 +569,18 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -574,7 +593,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= 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= diff --git a/graphqlserver/gqlgen.yml b/graphqlserver/gqlgen.yml new file mode 100644 index 00000000..fcbd0dc5 --- /dev/null +++ b/graphqlserver/gqlgen.yml @@ -0,0 +1,87 @@ +# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - graph/*.graphqls + +# Where should the generated server code go? +exec: + filename: graph/generated.go + package: graph + +# Uncomment to enable federation +# federation: +# filename: graph/federation.go +# package: graph + +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: graph + package: graph + filename_template: "{name}.resolvers.go" + # Optional: turn on to not generate template comments above resolvers + # omit_template_comment: false + +# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +# omit_slice_element_pointers: false + +# Optional: turn on to omit Is() methods to interface and unions +# omit_interface_checks : true + +# Optional: turn on to skip generation of ComplexityRoot struct content and Complexity function +# omit_complexity: false + +# Optional: turn on to not generate any file notice comments in generated files +# omit_gqlgen_file_notice: false + +# Optional: turn on to exclude the gqlgen version in the generated file notice. No effect if `omit_gqlgen_file_notice` is true. +# omit_gqlgen_version_in_file_notice: false + +# Optional: turn off to make struct-type struct fields not use pointers +# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing } +# struct_fields_always_pointers: true + +# Optional: turn off to make resolvers return values instead of pointers for structs +# resolvers_always_return_pointers: true + +# Optional: turn on to return pointers instead of values in unmarshalInput +# return_pointers_in_unmarshalinput: false + +# Optional: wrap nullable input fields with Omittable +# nullable_input_omittable: true + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# Optional: set to skip running `go mod tidy` when generating server code +# skip_mod_tidy: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +autobind: +# - "github.com/intelops/kubviz/graphqlserver/graph/model" + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go new file mode 100644 index 00000000..67512327 --- /dev/null +++ b/graphqlserver/graph/generated.go @@ -0,0 +1,5414 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package graph + +import ( + "bytes" + "context" + "embed" + "errors" + "fmt" + "strconv" + "sync" + "sync/atomic" + + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/introspection" + "github.com/intelops/kubviz/graphqlserver/graph/model" + gqlparser "github.com/vektah/gqlparser/v2" + "github.com/vektah/gqlparser/v2/ast" +) + +// region ************************** generated!.gotpl ************************** + +// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. +func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { + return &executableSchema{ + schema: cfg.Schema, + resolvers: cfg.Resolvers, + directives: cfg.Directives, + complexity: cfg.Complexity, + } +} + +type Config struct { + Schema *ast.Schema + Resolvers ResolverRoot + Directives DirectiveRoot + Complexity ComplexityRoot +} + +type ResolverRoot interface { + Query() QueryResolver +} + +type DirectiveRoot struct { +} + +type ComplexityRoot struct { + KubeScore struct { + APIVersion func(childComplexity int) int + ClusterName func(childComplexity int) int + Description func(childComplexity int) int + EventTime func(childComplexity int) int + FileName func(childComplexity int) int + FileRow func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + ObjectName func(childComplexity int) int + Path func(childComplexity int) int + Summary func(childComplexity int) int + TargetType func(childComplexity int) int + } + + NamespaceData struct { + KubeScores func(childComplexity int) int + Namespace func(childComplexity int) int + OutdatedImages func(childComplexity int) int + Resources func(childComplexity int) int + } + + OutdatedImage struct { + ClusterName func(childComplexity int) int + CurrentImage func(childComplexity int) int + CurrentTag func(childComplexity int) int + EventTime func(childComplexity int) int + LatestVersion func(childComplexity int) int + Namespace func(childComplexity int) int + Pod func(childComplexity int) int + VersionsBehind func(childComplexity int) int + } + + Query struct { + AllNamespaceData func(childComplexity int) int + } + + Resource struct { + Age func(childComplexity int) int + ClusterName func(childComplexity int) int + EventTime func(childComplexity int) int + Kind func(childComplexity int) int + Namespace func(childComplexity int) int + Resource func(childComplexity int) int + } +} + +type QueryResolver interface { + AllNamespaceData(ctx context.Context) ([]*model.NamespaceData, error) +} + +type executableSchema struct { + schema *ast.Schema + resolvers ResolverRoot + directives DirectiveRoot + complexity ComplexityRoot +} + +func (e *executableSchema) Schema() *ast.Schema { + if e.schema != nil { + return e.schema + } + return parsedSchema +} + +func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { + ec := executionContext{nil, e, 0, 0, nil} + _ = ec + switch typeName + "." + field { + + case "KubeScore.apiVersion": + if e.complexity.KubeScore.APIVersion == nil { + break + } + + return e.complexity.KubeScore.APIVersion(childComplexity), true + + case "KubeScore.clusterName": + if e.complexity.KubeScore.ClusterName == nil { + break + } + + return e.complexity.KubeScore.ClusterName(childComplexity), true + + case "KubeScore.description": + if e.complexity.KubeScore.Description == nil { + break + } + + return e.complexity.KubeScore.Description(childComplexity), true + + case "KubeScore.eventTime": + if e.complexity.KubeScore.EventTime == nil { + break + } + + return e.complexity.KubeScore.EventTime(childComplexity), true + + case "KubeScore.fileName": + if e.complexity.KubeScore.FileName == nil { + break + } + + return e.complexity.KubeScore.FileName(childComplexity), true + + case "KubeScore.fileRow": + if e.complexity.KubeScore.FileRow == nil { + break + } + + return e.complexity.KubeScore.FileRow(childComplexity), true + + case "KubeScore.id": + if e.complexity.KubeScore.ID == nil { + break + } + + return e.complexity.KubeScore.ID(childComplexity), true + + case "KubeScore.kind": + if e.complexity.KubeScore.Kind == nil { + break + } + + return e.complexity.KubeScore.Kind(childComplexity), true + + case "KubeScore.name": + if e.complexity.KubeScore.Name == nil { + break + } + + return e.complexity.KubeScore.Name(childComplexity), true + + case "KubeScore.namespace": + if e.complexity.KubeScore.Namespace == nil { + break + } + + return e.complexity.KubeScore.Namespace(childComplexity), true + + case "KubeScore.objectName": + if e.complexity.KubeScore.ObjectName == nil { + break + } + + return e.complexity.KubeScore.ObjectName(childComplexity), true + + case "KubeScore.path": + if e.complexity.KubeScore.Path == nil { + break + } + + return e.complexity.KubeScore.Path(childComplexity), true + + case "KubeScore.summary": + if e.complexity.KubeScore.Summary == nil { + break + } + + return e.complexity.KubeScore.Summary(childComplexity), true + + case "KubeScore.targetType": + if e.complexity.KubeScore.TargetType == nil { + break + } + + return e.complexity.KubeScore.TargetType(childComplexity), true + + case "NamespaceData.kubeScores": + if e.complexity.NamespaceData.KubeScores == nil { + break + } + + return e.complexity.NamespaceData.KubeScores(childComplexity), true + + case "NamespaceData.namespace": + if e.complexity.NamespaceData.Namespace == nil { + break + } + + return e.complexity.NamespaceData.Namespace(childComplexity), true + + case "NamespaceData.outdatedImages": + if e.complexity.NamespaceData.OutdatedImages == nil { + break + } + + return e.complexity.NamespaceData.OutdatedImages(childComplexity), true + + case "NamespaceData.resources": + if e.complexity.NamespaceData.Resources == nil { + break + } + + return e.complexity.NamespaceData.Resources(childComplexity), true + + case "OutdatedImage.clusterName": + if e.complexity.OutdatedImage.ClusterName == nil { + break + } + + return e.complexity.OutdatedImage.ClusterName(childComplexity), true + + case "OutdatedImage.currentImage": + if e.complexity.OutdatedImage.CurrentImage == nil { + break + } + + return e.complexity.OutdatedImage.CurrentImage(childComplexity), true + + case "OutdatedImage.currentTag": + if e.complexity.OutdatedImage.CurrentTag == nil { + break + } + + return e.complexity.OutdatedImage.CurrentTag(childComplexity), true + + case "OutdatedImage.eventTime": + if e.complexity.OutdatedImage.EventTime == nil { + break + } + + return e.complexity.OutdatedImage.EventTime(childComplexity), true + + case "OutdatedImage.latestVersion": + if e.complexity.OutdatedImage.LatestVersion == nil { + break + } + + return e.complexity.OutdatedImage.LatestVersion(childComplexity), true + + case "OutdatedImage.namespace": + if e.complexity.OutdatedImage.Namespace == nil { + break + } + + return e.complexity.OutdatedImage.Namespace(childComplexity), true + + case "OutdatedImage.pod": + if e.complexity.OutdatedImage.Pod == nil { + break + } + + return e.complexity.OutdatedImage.Pod(childComplexity), true + + case "OutdatedImage.versionsBehind": + if e.complexity.OutdatedImage.VersionsBehind == nil { + break + } + + return e.complexity.OutdatedImage.VersionsBehind(childComplexity), true + + case "Query.allNamespaceData": + if e.complexity.Query.AllNamespaceData == nil { + break + } + + return e.complexity.Query.AllNamespaceData(childComplexity), true + + case "Resource.age": + if e.complexity.Resource.Age == nil { + break + } + + return e.complexity.Resource.Age(childComplexity), true + + case "Resource.clusterName": + if e.complexity.Resource.ClusterName == nil { + break + } + + return e.complexity.Resource.ClusterName(childComplexity), true + + case "Resource.eventTime": + if e.complexity.Resource.EventTime == nil { + break + } + + return e.complexity.Resource.EventTime(childComplexity), true + + case "Resource.kind": + if e.complexity.Resource.Kind == nil { + break + } + + return e.complexity.Resource.Kind(childComplexity), true + + case "Resource.namespace": + if e.complexity.Resource.Namespace == nil { + break + } + + return e.complexity.Resource.Namespace(childComplexity), true + + case "Resource.resource": + if e.complexity.Resource.Resource == nil { + break + } + + return e.complexity.Resource.Resource(childComplexity), true + + } + return 0, false +} + +func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { + rc := graphql.GetOperationContext(ctx) + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} + inputUnmarshalMap := graphql.BuildUnmarshalerMap() + first := true + + switch rc.Operation.Operation { + case ast.Query: + return func(ctx context.Context) *graphql.Response { + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, rc.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { + result := <-ec.deferredResults + atomic.AddInt32(&ec.pendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } + } + var buf bytes.Buffer + data.MarshalGQL(&buf) + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext + } + + return &response + } + + default: + return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) + } +} + +type executionContext struct { + *graphql.OperationContext + *executableSchema + deferred int32 + pendingDeferred int32 + deferredResults chan graphql.DeferredResult +} + +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + +func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapSchema(ec.Schema()), nil +} + +func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil +} + +//go:embed "schema.graphqls" +var sourcesFS embed.FS + +func sourceData(filename string) string { + data, err := sourcesFS.ReadFile(filename) + if err != nil { + panic(fmt.Sprintf("codegen problem: %s not available", filename)) + } + return string(data) +} + +var sources = []*ast.Source{ + {Name: "schema.graphqls", Input: sourceData("schema.graphqls"), BuiltIn: false}, +} +var parsedSchema = gqlparser.MustLoadSchema(sources...) + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["name"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _KubeScore_id(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_objectName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ObjectName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_kind(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_apiVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.APIVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_name(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_targetType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.TargetType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_description(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_summary(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Summary, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_fileName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FileName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_fileRow(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FileRow, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_outdatedImages(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OutdatedImages, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.OutdatedImage) + fc.Result = res + return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_OutdatedImage_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_OutdatedImage_namespace(ctx, field) + case "pod": + return ec.fieldContext_OutdatedImage_pod(ctx, field) + case "currentImage": + return ec.fieldContext_OutdatedImage_currentImage(ctx, field) + case "currentTag": + return ec.fieldContext_OutdatedImage_currentTag(ctx, field) + case "latestVersion": + return ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + case "versionsBehind": + return ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + case "eventTime": + return ec.fieldContext_OutdatedImage_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_kubeScores(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.KubeScores, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.KubeScore) + fc.Result = res + return ec.marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_KubeScore_id(ctx, field) + case "clusterName": + return ec.fieldContext_KubeScore_clusterName(ctx, field) + case "objectName": + return ec.fieldContext_KubeScore_objectName(ctx, field) + case "kind": + return ec.fieldContext_KubeScore_kind(ctx, field) + case "apiVersion": + return ec.fieldContext_KubeScore_apiVersion(ctx, field) + case "name": + return ec.fieldContext_KubeScore_name(ctx, field) + case "namespace": + return ec.fieldContext_KubeScore_namespace(ctx, field) + case "targetType": + return ec.fieldContext_KubeScore_targetType(ctx, field) + case "description": + return ec.fieldContext_KubeScore_description(ctx, field) + case "path": + return ec.fieldContext_KubeScore_path(ctx, field) + case "summary": + return ec.fieldContext_KubeScore_summary(ctx, field) + case "fileName": + return ec.fieldContext_KubeScore_fileName(ctx, field) + case "fileRow": + return ec.fieldContext_KubeScore_fileRow(ctx, field) + case "eventTime": + return ec.fieldContext_KubeScore_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type KubeScore", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_resources(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_resources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Resources, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Resource) + fc.Result = res + return ec.marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_resources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_Resource_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_Resource_namespace(ctx, field) + case "kind": + return ec.fieldContext_Resource_kind(ctx, field) + case "resource": + return ec.fieldContext_Resource_resource(ctx, field) + case "age": + return ec.fieldContext_Resource_age(ctx, field) + case "eventTime": + return ec.fieldContext_Resource_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Resource", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_pod(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_pod(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Pod, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_pod(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_currentImage(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CurrentImage, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_currentTag(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CurrentTag, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.LatestVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VersionsBehind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allNamespaceData(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllNamespaceData(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.NamespaceData) + fc.Result = res + return ec.marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "namespace": + return ec.fieldContext_NamespaceData_namespace(ctx, field) + case "outdatedImages": + return ec.fieldContext_NamespaceData_outdatedImages(ctx, field) + case "kubeScores": + return ec.fieldContext_NamespaceData_kubeScores(ctx, field) + case "resources": + return ec.fieldContext_NamespaceData_resources(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type NamespaceData", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(fc.Args["name"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___schema(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + fc.Result = res + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "description": + return ec.fieldContext___Schema_description(ctx, field) + case "types": + return ec.fieldContext___Schema_types(ctx, field) + case "queryType": + return ec.fieldContext___Schema_queryType(ctx, field) + case "mutationType": + return ec.fieldContext___Schema_mutationType(ctx, field) + case "subscriptionType": + return ec.fieldContext___Schema_subscriptionType(ctx, field) + case "directives": + return ec.fieldContext___Schema_directives(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_resource(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_resource(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Resource, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_age(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Age, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_locations(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Locations, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_locations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type __DirectiveLocation does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_args(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsRepeatable, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_isDeprecated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_deprecationReason(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_args(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_isDeprecated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_deprecationReason(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_defaultValue(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DefaultValue, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_types(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Types(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_types(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_queryType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.QueryType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_queryType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_mutationType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MutationType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_mutationType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_subscriptionType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SubscriptionType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_directives(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Directives(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.Directive) + fc.Result = res + return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_directives(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___Directive_name(ctx, field) + case "description": + return ec.fieldContext___Directive_description(ctx, field) + case "locations": + return ec.fieldContext___Directive_locations(ctx, field) + case "args": + return ec.fieldContext___Directive_args(ctx, field) + case "isRepeatable": + return ec.fieldContext___Directive_isRepeatable(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Directive", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalN__TypeKind2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type __TypeKind does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_fields(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Fields(fc.Args["includeDeprecated"].(bool)), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Field) + fc.Result = res + return ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___Field_name(ctx, field) + case "description": + return ec.fieldContext___Field_description(ctx, field) + case "args": + return ec.fieldContext___Field_args(ctx, field) + case "type": + return ec.fieldContext___Field_type(ctx, field) + case "isDeprecated": + return ec.fieldContext___Field_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___Field_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Field", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_interfaces(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Interfaces(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_interfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_possibleTypes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PossibleTypes(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_possibleTypes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_enumValues(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EnumValues(fc.Args["includeDeprecated"].(bool)), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.EnumValue) + fc.Result = res + return ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___EnumValue_name(ctx, field) + case "description": + return ec.fieldContext___EnumValue_description(ctx, field) + case "isDeprecated": + return ec.fieldContext___EnumValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___EnumValue_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __EnumValue", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_inputFields(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.InputFields(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_inputFields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_ofType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OfType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_ofType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SpecifiedByURL(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var kubeScoreImplementors = []string{"KubeScore"} + +func (ec *executionContext) _KubeScore(ctx context.Context, sel ast.SelectionSet, obj *model.KubeScore) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, kubeScoreImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("KubeScore") + case "id": + out.Values[i] = ec._KubeScore_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._KubeScore_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "objectName": + out.Values[i] = ec._KubeScore_objectName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._KubeScore_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "apiVersion": + out.Values[i] = ec._KubeScore_apiVersion(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._KubeScore_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._KubeScore_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "targetType": + out.Values[i] = ec._KubeScore_targetType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec._KubeScore_description(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "path": + out.Values[i] = ec._KubeScore_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "summary": + out.Values[i] = ec._KubeScore_summary(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "fileName": + out.Values[i] = ec._KubeScore_fileName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "fileRow": + out.Values[i] = ec._KubeScore_fileRow(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventTime": + out.Values[i] = ec._KubeScore_eventTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var namespaceDataImplementors = []string{"NamespaceData"} + +func (ec *executionContext) _NamespaceData(ctx context.Context, sel ast.SelectionSet, obj *model.NamespaceData) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, namespaceDataImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("NamespaceData") + case "namespace": + out.Values[i] = ec._NamespaceData_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "outdatedImages": + out.Values[i] = ec._NamespaceData_outdatedImages(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kubeScores": + out.Values[i] = ec._NamespaceData_kubeScores(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resources": + out.Values[i] = ec._NamespaceData_resources(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var outdatedImageImplementors = []string{"OutdatedImage"} + +func (ec *executionContext) _OutdatedImage(ctx context.Context, sel ast.SelectionSet, obj *model.OutdatedImage) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, outdatedImageImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("OutdatedImage") + case "clusterName": + out.Values[i] = ec._OutdatedImage_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._OutdatedImage_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pod": + out.Values[i] = ec._OutdatedImage_pod(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "currentImage": + out.Values[i] = ec._OutdatedImage_currentImage(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "currentTag": + out.Values[i] = ec._OutdatedImage_currentTag(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "latestVersion": + out.Values[i] = ec._OutdatedImage_latestVersion(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "versionsBehind": + out.Values[i] = ec._OutdatedImage_versionsBehind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventTime": + out.Values[i] = ec._OutdatedImage_eventTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var queryImplementors = []string{"Query"} + +func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, queryImplementors) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Query", + }) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ + Object: field.Name, + Field: field, + }) + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Query") + case "allNamespaceData": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allNamespaceData(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "__type": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Query___type(ctx, field) + }) + case "__schema": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Query___schema(ctx, field) + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var resourceImplementors = []string{"Resource"} + +func (ec *executionContext) _Resource(ctx context.Context, sel ast.SelectionSet, obj *model.Resource) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, resourceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Resource") + case "clusterName": + out.Values[i] = ec._Resource_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._Resource_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._Resource_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resource": + out.Values[i] = ec._Resource_resource(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "age": + out.Values[i] = ec._Resource_age(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventTime": + out.Values[i] = ec._Resource_eventTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __DirectiveImplementors = []string{"__Directive"} + +func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Directive") + case "name": + out.Values[i] = ec.___Directive_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___Directive_description(ctx, field, obj) + case "locations": + out.Values[i] = ec.___Directive_locations(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "args": + out.Values[i] = ec.___Directive_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isRepeatable": + out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __EnumValueImplementors = []string{"__EnumValue"} + +func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__EnumValue") + case "name": + out.Values[i] = ec.___EnumValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___EnumValue_description(ctx, field, obj) + case "isDeprecated": + out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __FieldImplementors = []string{"__Field"} + +func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Field") + case "name": + out.Values[i] = ec.___Field_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___Field_description(ctx, field, obj) + case "args": + out.Values[i] = ec.___Field_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "type": + out.Values[i] = ec.___Field_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isDeprecated": + out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __InputValueImplementors = []string{"__InputValue"} + +func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__InputValue") + case "name": + out.Values[i] = ec.___InputValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___InputValue_description(ctx, field, obj) + case "type": + out.Values[i] = ec.___InputValue_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "defaultValue": + out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __SchemaImplementors = []string{"__Schema"} + +func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Schema") + case "description": + out.Values[i] = ec.___Schema_description(ctx, field, obj) + case "types": + out.Values[i] = ec.___Schema_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "queryType": + out.Values[i] = ec.___Schema_queryType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "mutationType": + out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) + case "subscriptionType": + out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) + case "directives": + out.Values[i] = ec.___Schema_directives(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __TypeImplementors = []string{"__Type"} + +func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Type") + case "kind": + out.Values[i] = ec.___Type_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec.___Type_name(ctx, field, obj) + case "description": + out.Values[i] = ec.___Type_description(ctx, field, obj) + case "fields": + out.Values[i] = ec.___Type_fields(ctx, field, obj) + case "interfaces": + out.Values[i] = ec.___Type_interfaces(ctx, field, obj) + case "possibleTypes": + out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) + case "enumValues": + out.Values[i] = ec.___Type_enumValues(ctx, field, obj) + case "inputFields": + out.Values[i] = ec.___Type_inputFields(ctx, field, obj) + case "ofType": + out.Values[i] = ec.___Type_ofType(ctx, field, obj) + case "specifiedByURL": + out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + res, err := graphql.UnmarshalBoolean(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + res := graphql.MarshalBoolean(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalID(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalID(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { + res, err := graphql.UnmarshalInt(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + res := graphql.MarshalInt(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.KubeScore) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNKubeScore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScore(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNKubeScore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScore(ctx context.Context, sel ast.SelectionSet, v *model.KubeScore) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._KubeScore(ctx, sel, v) +} + +func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NamespaceData) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx context.Context, sel ast.SelectionSet, v *model.NamespaceData) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._NamespaceData(ctx, sel, v) +} + +func (ec *executionContext) marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.OutdatedImage) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNOutdatedImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImage(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNOutdatedImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImage(ctx context.Context, sel ast.SelectionSet, v *model.OutdatedImage) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._OutdatedImage(ctx, sel, v) +} + +func (ec *executionContext) marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Resource) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResource(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResource(ctx context.Context, sel ast.SelectionSet, v *model.Resource) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Resource(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { + return ec.___Directive(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Directive) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalN__DirectiveLocation2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__DirectiveLocation2string(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx context.Context, sel ast.SelectionSet, v introspection.EnumValue) graphql.Marshaler { + return ec.___EnumValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx context.Context, sel ast.SelectionSet, v introspection.Field) graphql.Marshaler { + return ec.___Field(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v introspection.InputValue) graphql.Marshaler { + return ec.___InputValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler { + return ec.___Type(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +func (ec *executionContext) unmarshalN__TypeKind2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + res, err := graphql.UnmarshalBoolean(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + res := graphql.MarshalBoolean(v) + return res +} + +func (ec *executionContext) unmarshalOBoolean2ᚖbool(ctx context.Context, v interface{}) (*bool, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalBoolean(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast.SelectionSet, v *bool) graphql.Marshaler { + if v == nil { + return graphql.Null + } + res := graphql.MarshalBoolean(*v) + return res +} + +func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalString(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + res := graphql.MarshalString(*v) + return res +} + +func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Field) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v *introspection.Schema) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Schema(ctx, sel, v) +} + +func (ec *executionContext) marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +// endregion ***************************** type.gotpl ***************************** diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go new file mode 100644 index 00000000..0e97567e --- /dev/null +++ b/graphqlserver/graph/model/models_gen.go @@ -0,0 +1,50 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package model + +type KubeScore struct { + ID string `json:"id"` + ClusterName string `json:"clusterName"` + ObjectName string `json:"objectName"` + Kind string `json:"kind"` + APIVersion string `json:"apiVersion"` + Name string `json:"name"` + Namespace string `json:"namespace"` + TargetType string `json:"targetType"` + Description string `json:"description"` + Path string `json:"path"` + Summary string `json:"summary"` + FileName string `json:"fileName"` + FileRow int `json:"fileRow"` + EventTime string `json:"eventTime"` +} + +type NamespaceData struct { + Namespace string `json:"namespace"` + OutdatedImages []*OutdatedImage `json:"outdatedImages"` + KubeScores []*KubeScore `json:"kubeScores"` + Resources []*Resource `json:"resources"` +} + +type OutdatedImage struct { + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + Pod string `json:"pod"` + CurrentImage string `json:"currentImage"` + CurrentTag string `json:"currentTag"` + LatestVersion string `json:"latestVersion"` + VersionsBehind int `json:"versionsBehind"` + EventTime string `json:"eventTime"` +} + +type Query struct { +} + +type Resource struct { + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + Kind string `json:"kind"` + Resource string `json:"resource"` + Age string `json:"age"` + EventTime string `json:"eventTime"` +} diff --git a/graphqlserver/graph/resolver.go b/graphqlserver/graph/resolver.go new file mode 100644 index 00000000..a940582c --- /dev/null +++ b/graphqlserver/graph/resolver.go @@ -0,0 +1,31 @@ +package graph + +import ( + "database/sql" + "log" + + "github.com/intelops/kubviz/client/pkg/clickhouse" + "github.com/intelops/kubviz/client/pkg/config" + "github.com/kelseyhightower/envconfig" +) + +// This file will not be regenerated automatically. +// +// It serves as dependency injection for your app, add any dependencies you require here. + +type Resolver struct { + DB *sql.DB +} + +func NewResolver() *Resolver { + log.Println("Client Application started...") + cfg := &config.Config{} + if err := envconfig.Process("", cfg); err != nil { + log.Fatalf("Could not parse env Config: %v", err) + } + _, db, err := clickhouse.NewDBClient(cfg) + if err != nil { + log.Fatal(err) + } + return &Resolver{DB: db} +} diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls new file mode 100644 index 00000000..2451272c --- /dev/null +++ b/graphqlserver/graph/schema.graphqls @@ -0,0 +1,47 @@ +type Query { + allNamespaceData: [NamespaceData!]! +} + +type NamespaceData { + namespace: String! + outdatedImages: [OutdatedImage!]! + kubeScores: [KubeScore!]! + resources: [Resource!]! +} + +type OutdatedImage { + clusterName: String! + namespace: String! + pod: String! + currentImage: String! + currentTag: String! + latestVersion: String! + versionsBehind: Int! + eventTime: String! +} + +type KubeScore { + id: ID! + clusterName: String! + objectName: String! + kind: String! + apiVersion: String! + name: String! + namespace: String! + targetType: String! + description: String! + path: String! + summary: String! + fileName: String! + fileRow: Int! + eventTime: String! +} + +type Resource { + clusterName: String! + namespace: String! + kind: String! + resource: String! + age: String! + eventTime: String! +} diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go new file mode 100644 index 00000000..c94b2870 --- /dev/null +++ b/graphqlserver/graph/schema.resolvers.go @@ -0,0 +1,158 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.42 + +import ( + "context" + "fmt" + + "github.com/intelops/kubviz/graphqlserver/graph/model" +) + +func (r *queryResolver) AllNamespaceData(ctx context.Context) ([]*model.NamespaceData, error) { + + var namespaceDataList []*model.NamespaceData + + resolver := NewResolver() + + namespaces, err := r.fetchNamespacesFromDatabase(ctx) + if err != nil { + return nil, fmt.Errorf("error fetching namespaces: %v", err) + } + + for _, ns := range namespaces { + outdatedImages, err := resolver.fetchOutdatedImages(ctx, ns) + if err != nil { + return nil, fmt.Errorf("error fetching outdated images for namespace %s: %v", ns, err) + } + + kubeScores, err := resolver.fetchKubeScores(ctx, ns) + if err != nil { + return nil, fmt.Errorf("error fetching kube scores for namespace %s: %v", ns, err) + } + + resources, err := resolver.fetchResources(ctx, ns) + if err != nil { + return nil, fmt.Errorf("error fetching resources for namespace %s: %v", ns, err) + } + + nd := &model.NamespaceData{ + Namespace: ns, + OutdatedImages: outdatedImages, + KubeScores: kubeScores, + Resources: resources, + } + + namespaceDataList = append(namespaceDataList, nd) + } + + return namespaceDataList, nil +} +func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, error) { + + query := `SELECT DISTINCT Namespace FROM events` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var namespaces []string + for rows.Next() { + var namespace string + if err := rows.Scan(&namespace); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + namespaces = append(namespaces, namespace) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return namespaces, nil +} +func (r *Resolver) fetchOutdatedImages(ctx context.Context, namespace string) ([]*model.OutdatedImage, error) { + + query := `SELECT ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime FROM outdated_images WHERE Namespace = ?` + + rows, err := r.DB.QueryContext(ctx, query, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var outdatedImages []*model.OutdatedImage + for rows.Next() { + var oi model.OutdatedImage + if err := rows.Scan(&oi.ClusterName, &oi.Namespace, &oi.Pod, &oi.CurrentImage, &oi.CurrentTag, &oi.LatestVersion, &oi.VersionsBehind, &oi.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + outdatedImages = append(outdatedImages, &oi) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return outdatedImages, nil +} + +func (r *Resolver) fetchKubeScores(ctx context.Context, namespace string) ([]*model.KubeScore, error) { + + query := `SELECT id, ClusterName, ObjectName, Kind, ApiVersion, Name, Namespace, TargetType, Description, Path, Summary, FileName, FileRow, EventTime FROM kubescore WHERE Namespace = ?` + + rows, err := r.DB.QueryContext(ctx, query, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var kubeScores []*model.KubeScore + for rows.Next() { + var ks model.KubeScore + if err := rows.Scan(&ks.ID, &ks.ClusterName, &ks.ObjectName, &ks.Kind, &ks.APIVersion, &ks.Name, &ks.Namespace, &ks.TargetType, &ks.Description, &ks.Path, &ks.Summary, &ks.FileName, &ks.FileRow, &ks.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + kubeScores = append(kubeScores, &ks) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return kubeScores, nil +} +func (r *Resolver) fetchResources(ctx context.Context, namespace string) ([]*model.Resource, error) { + + query := `SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime FROM getall_resources WHERE Namespace = ?` + + rows, err := r.DB.QueryContext(ctx, query, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var resources []*model.Resource + for rows.Next() { + var res model.Resource + if err := rows.Scan(&res.ClusterName, &res.Namespace, &res.Kind, &res.Resource, &res.Age, &res.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + resources = append(resources, &res) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return resources, nil +} + +// Query returns QueryResolver implementation. +func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } + +type queryResolver struct{ *Resolver } diff --git a/graphqlserver/server.go b/graphqlserver/server.go new file mode 100644 index 00000000..85282e33 --- /dev/null +++ b/graphqlserver/server.go @@ -0,0 +1,28 @@ +package main + +import ( + "log" + "net/http" + "os" + + "github.com/99designs/gqlgen/graphql/handler" + "github.com/99designs/gqlgen/graphql/playground" + "github.com/intelops/kubviz/graphqlserver/graph" +) + +const defaultPort = "8085" + +func main() { + port := os.Getenv("PORT") + if port == "" { + port = defaultPort + } + + srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) + + http.Handle("/", playground.Handler("GraphQL playground", "/query")) + http.Handle("/query", srv) + + log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) + log.Fatal(http.ListenAndServe(":"+port, nil)) +} diff --git a/steps-to-test.txt b/steps-to-test.txt index 1134464b..ede5c767 100644 --- a/steps-to-test.txt +++ b/steps-to-test.txt @@ -33,5 +33,46 @@ docker tag ubuntu:latest localhost:5001/ubuntu:v1 docker push localhost:5001/ubuntu:v1 -# test commit -# test mr build + +tables connected with namespace in common: outdated , kubescore , resources + +sample query: +query { + allNamespaceData { + namespace + outdatedImages { + clusterName + namespace + pod + currentImage + currentTag + latestVersion + versionsBehind + eventTime + } + kubeScores { + id + clusterName + objectName + kind + apiVersion + name + namespace + targetType + description + path + summary + fileName + fileRow + eventTime + } + resources { + clusterName + namespace + kind + resource + age + eventTime + } + } + } From 129507ce409016cdfcf7464c3bbd66e36c6b29a5 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 16 Jan 2024 17:40:56 +0530 Subject: [PATCH 181/263] sample output for graphql query --- steps-to-test.txt | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/steps-to-test.txt b/steps-to-test.txt index ede5c767..8aee4a19 100644 --- a/steps-to-test.txt +++ b/steps-to-test.txt @@ -76,3 +76,60 @@ query { } } } + + + sample response: + + { + "data": { + "allNamespaceData": [ + { + "namespace": "namespace1", + "outdatedImages": [ + { + "clusterName": "cluster1", + "namespace": "namespace1", + "pod": "pod1", + "currentImage": "image1:v1", + "currentTag": "v1", + "latestVersion": "v2", + "versionsBehind": 1, + "eventTime": "2022-01-01T12:00:00Z" + }, + // ... more outdatedImages for namespace1 ... + ], + "kubeScores": [ + { + "id": "ks1", + "clusterName": "cluster1", + "objectName": "object1", + "kind": "Deployment", + "apiVersion": "v1", + "name": "deployment1", + "namespace": "namespace1", + "targetType": "type1", + "description": "description1", + "path": "path1", + "summary": "summary1", + "fileName": "file1", + "fileRow": 10, + "eventTime": "2022-01-01T12:00:00Z" + }, + // ... more kubeScores for namespace1 ... + ], + "resources": [ + { + "clusterName": "cluster1", + "namespace": "namespace1", + "kind": "Pod", + "resource": "pod1", + "age": "10d", + "eventTime": "2022-01-01T12:00:00Z" + }, + // ... more resources for namespace1 ... + ] + }, + // ... more namespace data objects ... + ] + } + } From c337af9606094b53fb70acc256d306295cda75bc Mon Sep 17 00:00:00 2001 From: vijeyash Date: Thu, 18 Jan 2024 21:31:38 +0530 Subject: [PATCH 182/263] error check included for db instanciation --- Dockerfile | 11 +++++++++++ dockerfiles/graphqlserver/Dockerfile | 3 +-- graphqlserver/graph/schema.resolvers.go | 16 ++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..740b2c36 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.19 AS builder +WORKDIR / +COPY ./ ./ + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o ./build/graphqlserver graphqlserver/server.go + +FROM scratch +COPY --from=builder ./build/graphqlserver server + +USER 65532:65532 +ENTRYPOINT ["/server"] diff --git a/dockerfiles/graphqlserver/Dockerfile b/dockerfiles/graphqlserver/Dockerfile index 8b0e8111..740b2c36 100644 --- a/dockerfiles/graphqlserver/Dockerfile +++ b/dockerfiles/graphqlserver/Dockerfile @@ -2,8 +2,7 @@ FROM golang:1.19 AS builder WORKDIR / COPY ./ ./ -RUN go mod download -RUN CGO_ENABLED=0 go build -o ./build/graphqlserver graphqlserver/server.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o ./build/graphqlserver graphqlserver/server.go FROM scratch COPY --from=builder ./build/graphqlserver server diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index c94b2870..ff52def3 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -51,7 +51,9 @@ func (r *queryResolver) AllNamespaceData(ctx context.Context) ([]*model.Namespac return namespaceDataList, nil } func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, error) { - + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } query := `SELECT DISTINCT Namespace FROM events` rows, err := r.DB.QueryContext(ctx, query) @@ -76,7 +78,9 @@ func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, e return namespaces, nil } func (r *Resolver) fetchOutdatedImages(ctx context.Context, namespace string) ([]*model.OutdatedImage, error) { - + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } query := `SELECT ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime FROM outdated_images WHERE Namespace = ?` rows, err := r.DB.QueryContext(ctx, query, namespace) @@ -102,7 +106,9 @@ func (r *Resolver) fetchOutdatedImages(ctx context.Context, namespace string) ([ } func (r *Resolver) fetchKubeScores(ctx context.Context, namespace string) ([]*model.KubeScore, error) { - + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } query := `SELECT id, ClusterName, ObjectName, Kind, ApiVersion, Name, Namespace, TargetType, Description, Path, Summary, FileName, FileRow, EventTime FROM kubescore WHERE Namespace = ?` rows, err := r.DB.QueryContext(ctx, query, namespace) @@ -127,7 +133,9 @@ func (r *Resolver) fetchKubeScores(ctx context.Context, namespace string) ([]*mo return kubeScores, nil } func (r *Resolver) fetchResources(ctx context.Context, namespace string) ([]*model.Resource, error) { - + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } query := `SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime FROM getall_resources WHERE Namespace = ?` rows, err := r.DB.QueryContext(ctx, query, namespace) From 95815134db45de6dde0f53e79061d4c00d255f9c Mon Sep 17 00:00:00 2001 From: alanjino Date: Thu, 18 Jan 2024 22:09:09 +0530 Subject: [PATCH 183/263] helm chart changes with evn variables of OTEL --- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 18 ++++++++++++++++++ charts/agent/values.yaml | 9 +++++++++ charts/client/Chart.yaml | 2 +- charts/client/templates/deployment.yaml | 6 ++++++ charts/client/values.yaml | 6 ++++++ 6 files changed, 41 insertions(+), 2 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 70adb583..237225e5 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.10 +version: 1.1.11 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index 05a8abd3..acaa05a5 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -75,6 +75,12 @@ spec: value: "{{ .Values.schedule.kubepreupgradeInterval }}" - name: TRIVY_INTERVAL value: "{{ .Values.schedule.trivyInterval }}" + - name: IS_OPTEL_ENABLED + value: "{{ .Values.opentelemetry.isEnabled }}" + - name : OPTEL_URL + value: {{ .Values.opentelemetry.url }} + - name : APPLICATION_NAME + value : {{ .Values.opentelemetry.appName }} {{- if .Values.persistence.enabled }} volumeMounts: - name: data @@ -115,6 +121,12 @@ spec: {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} + - name: IS_OPTEL_ENABLED + value: "{{ .Values.opentelemetry.isEnabled }}" + - name : OPTEL_URL + value: {{ .Values.opentelemetry.url }} + - name : APPLICATION_NAME + value : {{ .Values.opentelemetry.appName }} {{- if .Values.git_bridge.persistence.enabled }} volumeMounts: - name: data @@ -156,6 +168,12 @@ spec: {{- end }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} + - name: IS_OPTEL_ENABLED + value: "{{ .Values.opentelemetry.isEnabled }}" + - name : OPTEL_URL + value: {{ .Values.opentelemetry.url }} + - name : APPLICATION_NAME + value : {{ .Values.opentelemetry.appName }} {{- if .Values.container_bridge.persistence.enabled }} volumeMounts: - name: data diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 733927f0..916f6d57 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -44,6 +44,8 @@ service: type: ClusterIP port: 80 + + git_bridge: enabled: false image: @@ -87,6 +89,7 @@ git_bridge: + container_bridge: enabled: false image: @@ -128,6 +131,7 @@ container_bridge: # hosts: # - chart-example.local + ingress: enabled: false annotations: {} @@ -182,6 +186,11 @@ schedule: kubepreupgradeInterval: "@every 22h" trivyInterval: "@every 24h" +opentelemetry: + isEnabled: false + url: "otelcollector.local" + appName: "kubviz" + clusterName: "kubviz" nats: host: kubviz-client-nats diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index bf6dfe4f..eb21a4d7 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.15 +version: 1.1.16 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index 64075db4..c13e6f84 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -139,6 +139,12 @@ spec: value: "{{ .Values.ttl.ttlInterval }}" - name: TTL_UNIT value: {{ .Values.ttl.ttlUnit }} + - name: IS_OPTEL_ENABLED + value: "{{ .Values.opentelemetry.isEnabled }}" + - name : OPTEL_URL + value: {{ .Values.opentelemetry.url }} + - name : APPLICATION_NAME + value : {{ .Values.opentelemetry.appName }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/client/values.yaml b/charts/client/values.yaml index aa272cbc..b73fe34b 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -168,3 +168,9 @@ migration: ttl: ttlInterval: "1" ttlUnit: MONTH + +opentelemetry: + isEnabled: false + url: "otelcollector.local" + appName: "kubviz" + From 5a6cd83c929b799e685f5dcf0b1b3f49ca784c87 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 22 Jan 2024 12:04:38 +0530 Subject: [PATCH 184/263] graphql implementations --- graphqlserver/graph/generated.go | 13715 +++++++++++++++++----- graphqlserver/graph/model/models_gen.go | 149 + graphqlserver/graph/resolver.go | 16 +- graphqlserver/graph/schema.graphqls | 159 + graphqlserver/graph/schema.resolvers.go | 311 +- graphqlserver/graph/utils.go | 129 + graphqlserver/server.go | 39 +- 7 files changed, 11623 insertions(+), 2895 deletions(-) create mode 100644 graphqlserver/graph/utils.go diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index 67512327..8d6f330b 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -46,6 +46,57 @@ type DirectiveRoot struct { } type ComplexityRoot struct { + DeletedAPI struct { + ClusterName func(childComplexity int) int + Deleted func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + Group func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + ObjectName func(childComplexity int) int + Scope func(childComplexity int) int + Version func(childComplexity int) int + } + + DeprecatedAPI struct { + ClusterName func(childComplexity int) int + Deprecated func(childComplexity int) int + Description func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + Kind func(childComplexity int) int + ObjectName func(childComplexity int) int + Scope func(childComplexity int) int + } + + Event struct { + ClusterName func(childComplexity int) int + Event func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + FirstTime func(childComplexity int) int + Host func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + LastTime func(childComplexity int) int + Message func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + OpType func(childComplexity int) int + Reason func(childComplexity int) int + } + + GetAllResource struct { + Age func(childComplexity int) int + ClusterName func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + Kind func(childComplexity int) int + Namespace func(childComplexity int) int + Resource func(childComplexity int) int + } + KubeScore struct { APIVersion func(childComplexity int) int ClusterName func(childComplexity int) int @@ -63,6 +114,24 @@ type ComplexityRoot struct { TargetType func(childComplexity int) int } + Kubescore struct { + APIVersion func(childComplexity int) int + ClusterName func(childComplexity int) int + Description func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + FileName func(childComplexity int) int + FileRow func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + ObjectName func(childComplexity int) int + Path func(childComplexity int) int + Summary func(childComplexity int) int + TargetType func(childComplexity int) int + } + NamespaceData struct { KubeScores func(childComplexity int) int Namespace func(childComplexity int) int @@ -82,7 +151,28 @@ type ComplexityRoot struct { } Query struct { - AllNamespaceData func(childComplexity int) int + AllDeletedAPIs func(childComplexity int) int + AllDeprecatedAPIs func(childComplexity int) int + AllEvents func(childComplexity int) int + AllGetAllResources func(childComplexity int) int + AllKubeScores func(childComplexity int) int + AllNamespaceData func(childComplexity int) int + AllRakkess func(childComplexity int) int + AllTrivyImages func(childComplexity int) int + AllTrivyMisconfigs func(childComplexity int) int + AllTrivySBOMs func(childComplexity int) int + AllTrivyVuls func(childComplexity int) int + } + + Rakkess struct { + ClusterName func(childComplexity int) int + Create func(childComplexity int) int + Delete func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + List func(childComplexity int) int + Name func(childComplexity int) int + Update func(childComplexity int) int } Resource struct { @@ -93,10 +183,89 @@ type ComplexityRoot struct { Namespace func(childComplexity int) int Resource func(childComplexity int) int } + + TrivyImage struct { + ArtifactName func(childComplexity int) int + ClusterName func(childComplexity int) int + ExpiryDate func(childComplexity int) int + ID func(childComplexity int) int + VulFixedVersion func(childComplexity int) int + VulID func(childComplexity int) int + VulInstalledVersion func(childComplexity int) int + VulLastModifiedDate func(childComplexity int) int + VulPkgID func(childComplexity int) int + VulPkgName func(childComplexity int) int + VulPublishedDate func(childComplexity int) int + VulSeverity func(childComplexity int) int + VulTitle func(childComplexity int) int + } + + TrivyMisconfig struct { + ClusterName func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + MisconfigAvdid func(childComplexity int) int + MisconfigDesc func(childComplexity int) int + MisconfigID func(childComplexity int) int + MisconfigMsg func(childComplexity int) int + MisconfigQuery func(childComplexity int) int + MisconfigResolution func(childComplexity int) int + MisconfigSeverity func(childComplexity int) int + MisconfigStatus func(childComplexity int) int + MisconfigTitle func(childComplexity int) int + MisconfigType func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + } + + TrivySBOM struct { + BomFormat func(childComplexity int) int + BomRef func(childComplexity int) int + ClusterName func(childComplexity int) int + ExpiryDate func(childComplexity int) int + ID func(childComplexity int) int + ImageName func(childComplexity int) int + PackageName func(childComplexity int) int + PackageURL func(childComplexity int) int + SerialNumber func(childComplexity int) int + Version func(childComplexity int) int + } + + TrivyVul struct { + ClusterName func(childComplexity int) int + ExpiryDate func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + VulFixedVersion func(childComplexity int) int + VulID func(childComplexity int) int + VulInstalledVersion func(childComplexity int) int + VulLastModifiedDate func(childComplexity int) int + VulPkgID func(childComplexity int) int + VulPkgName func(childComplexity int) int + VulPkgPath func(childComplexity int) int + VulPublishedDate func(childComplexity int) int + VulSeverity func(childComplexity int) int + VulTitle func(childComplexity int) int + VulVendorIds func(childComplexity int) int + } } type QueryResolver interface { AllNamespaceData(ctx context.Context) ([]*model.NamespaceData, error) + AllEvents(ctx context.Context) ([]*model.Event, error) + AllRakkess(ctx context.Context) ([]*model.Rakkess, error) + AllDeprecatedAPIs(ctx context.Context) ([]*model.DeprecatedAPI, error) + AllDeletedAPIs(ctx context.Context) ([]*model.DeletedAPI, error) + AllGetAllResources(ctx context.Context) ([]*model.GetAllResource, error) + AllTrivySBOMs(ctx context.Context) ([]*model.TrivySbom, error) + AllTrivyImages(ctx context.Context) ([]*model.TrivyImage, error) + AllKubeScores(ctx context.Context) ([]*model.Kubescore, error) + AllTrivyVuls(ctx context.Context) ([]*model.TrivyVul, error) + AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyMisconfig, error) } type executableSchema struct { @@ -118,6 +287,279 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "DeletedAPI.ClusterName": + if e.complexity.DeletedAPI.ClusterName == nil { + break + } + + return e.complexity.DeletedAPI.ClusterName(childComplexity), true + + case "DeletedAPI.Deleted": + if e.complexity.DeletedAPI.Deleted == nil { + break + } + + return e.complexity.DeletedAPI.Deleted(childComplexity), true + + case "DeletedAPI.EventTime": + if e.complexity.DeletedAPI.EventTime == nil { + break + } + + return e.complexity.DeletedAPI.EventTime(childComplexity), true + + case "DeletedAPI.ExpiryDate": + if e.complexity.DeletedAPI.ExpiryDate == nil { + break + } + + return e.complexity.DeletedAPI.ExpiryDate(childComplexity), true + + case "DeletedAPI.Group": + if e.complexity.DeletedAPI.Group == nil { + break + } + + return e.complexity.DeletedAPI.Group(childComplexity), true + + case "DeletedAPI.Kind": + if e.complexity.DeletedAPI.Kind == nil { + break + } + + return e.complexity.DeletedAPI.Kind(childComplexity), true + + case "DeletedAPI.Name": + if e.complexity.DeletedAPI.Name == nil { + break + } + + return e.complexity.DeletedAPI.Name(childComplexity), true + + case "DeletedAPI.ObjectName": + if e.complexity.DeletedAPI.ObjectName == nil { + break + } + + return e.complexity.DeletedAPI.ObjectName(childComplexity), true + + case "DeletedAPI.Scope": + if e.complexity.DeletedAPI.Scope == nil { + break + } + + return e.complexity.DeletedAPI.Scope(childComplexity), true + + case "DeletedAPI.Version": + if e.complexity.DeletedAPI.Version == nil { + break + } + + return e.complexity.DeletedAPI.Version(childComplexity), true + + case "DeprecatedAPI.ClusterName": + if e.complexity.DeprecatedAPI.ClusterName == nil { + break + } + + return e.complexity.DeprecatedAPI.ClusterName(childComplexity), true + + case "DeprecatedAPI.Deprecated": + if e.complexity.DeprecatedAPI.Deprecated == nil { + break + } + + return e.complexity.DeprecatedAPI.Deprecated(childComplexity), true + + case "DeprecatedAPI.Description": + if e.complexity.DeprecatedAPI.Description == nil { + break + } + + return e.complexity.DeprecatedAPI.Description(childComplexity), true + + case "DeprecatedAPI.EventTime": + if e.complexity.DeprecatedAPI.EventTime == nil { + break + } + + return e.complexity.DeprecatedAPI.EventTime(childComplexity), true + + case "DeprecatedAPI.ExpiryDate": + if e.complexity.DeprecatedAPI.ExpiryDate == nil { + break + } + + return e.complexity.DeprecatedAPI.ExpiryDate(childComplexity), true + + case "DeprecatedAPI.Kind": + if e.complexity.DeprecatedAPI.Kind == nil { + break + } + + return e.complexity.DeprecatedAPI.Kind(childComplexity), true + + case "DeprecatedAPI.ObjectName": + if e.complexity.DeprecatedAPI.ObjectName == nil { + break + } + + return e.complexity.DeprecatedAPI.ObjectName(childComplexity), true + + case "DeprecatedAPI.Scope": + if e.complexity.DeprecatedAPI.Scope == nil { + break + } + + return e.complexity.DeprecatedAPI.Scope(childComplexity), true + + case "Event.ClusterName": + if e.complexity.Event.ClusterName == nil { + break + } + + return e.complexity.Event.ClusterName(childComplexity), true + + case "Event.Event": + if e.complexity.Event.Event == nil { + break + } + + return e.complexity.Event.Event(childComplexity), true + + case "Event.EventTime": + if e.complexity.Event.EventTime == nil { + break + } + + return e.complexity.Event.EventTime(childComplexity), true + + case "Event.ExpiryDate": + if e.complexity.Event.ExpiryDate == nil { + break + } + + return e.complexity.Event.ExpiryDate(childComplexity), true + + case "Event.FirstTime": + if e.complexity.Event.FirstTime == nil { + break + } + + return e.complexity.Event.FirstTime(childComplexity), true + + case "Event.Host": + if e.complexity.Event.Host == nil { + break + } + + return e.complexity.Event.Host(childComplexity), true + + case "Event.Id": + if e.complexity.Event.ID == nil { + break + } + + return e.complexity.Event.ID(childComplexity), true + + case "Event.Kind": + if e.complexity.Event.Kind == nil { + break + } + + return e.complexity.Event.Kind(childComplexity), true + + case "Event.LastTime": + if e.complexity.Event.LastTime == nil { + break + } + + return e.complexity.Event.LastTime(childComplexity), true + + case "Event.Message": + if e.complexity.Event.Message == nil { + break + } + + return e.complexity.Event.Message(childComplexity), true + + case "Event.Name": + if e.complexity.Event.Name == nil { + break + } + + return e.complexity.Event.Name(childComplexity), true + + case "Event.Namespace": + if e.complexity.Event.Namespace == nil { + break + } + + return e.complexity.Event.Namespace(childComplexity), true + + case "Event.OpType": + if e.complexity.Event.OpType == nil { + break + } + + return e.complexity.Event.OpType(childComplexity), true + + case "Event.Reason": + if e.complexity.Event.Reason == nil { + break + } + + return e.complexity.Event.Reason(childComplexity), true + + case "GetAllResource.Age": + if e.complexity.GetAllResource.Age == nil { + break + } + + return e.complexity.GetAllResource.Age(childComplexity), true + + case "GetAllResource.ClusterName": + if e.complexity.GetAllResource.ClusterName == nil { + break + } + + return e.complexity.GetAllResource.ClusterName(childComplexity), true + + case "GetAllResource.EventTime": + if e.complexity.GetAllResource.EventTime == nil { + break + } + + return e.complexity.GetAllResource.EventTime(childComplexity), true + + case "GetAllResource.ExpiryDate": + if e.complexity.GetAllResource.ExpiryDate == nil { + break + } + + return e.complexity.GetAllResource.ExpiryDate(childComplexity), true + + case "GetAllResource.Kind": + if e.complexity.GetAllResource.Kind == nil { + break + } + + return e.complexity.GetAllResource.Kind(childComplexity), true + + case "GetAllResource.Namespace": + if e.complexity.GetAllResource.Namespace == nil { + break + } + + return e.complexity.GetAllResource.Namespace(childComplexity), true + + case "GetAllResource.Resource": + if e.complexity.GetAllResource.Resource == nil { + break + } + + return e.complexity.GetAllResource.Resource(childComplexity), true + case "KubeScore.apiVersion": if e.complexity.KubeScore.APIVersion == nil { break @@ -216,1867 +658,932 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.KubeScore.TargetType(childComplexity), true - case "NamespaceData.kubeScores": - if e.complexity.NamespaceData.KubeScores == nil { + case "Kubescore.apiVersion": + if e.complexity.Kubescore.APIVersion == nil { break } - return e.complexity.NamespaceData.KubeScores(childComplexity), true + return e.complexity.Kubescore.APIVersion(childComplexity), true - case "NamespaceData.namespace": - if e.complexity.NamespaceData.Namespace == nil { + case "Kubescore.clusterName": + if e.complexity.Kubescore.ClusterName == nil { break } - return e.complexity.NamespaceData.Namespace(childComplexity), true + return e.complexity.Kubescore.ClusterName(childComplexity), true - case "NamespaceData.outdatedImages": - if e.complexity.NamespaceData.OutdatedImages == nil { + case "Kubescore.description": + if e.complexity.Kubescore.Description == nil { break } - return e.complexity.NamespaceData.OutdatedImages(childComplexity), true + return e.complexity.Kubescore.Description(childComplexity), true - case "NamespaceData.resources": - if e.complexity.NamespaceData.Resources == nil { + case "Kubescore.eventTime": + if e.complexity.Kubescore.EventTime == nil { break } - return e.complexity.NamespaceData.Resources(childComplexity), true + return e.complexity.Kubescore.EventTime(childComplexity), true - case "OutdatedImage.clusterName": - if e.complexity.OutdatedImage.ClusterName == nil { + case "Kubescore.expiryDate": + if e.complexity.Kubescore.ExpiryDate == nil { break } - return e.complexity.OutdatedImage.ClusterName(childComplexity), true + return e.complexity.Kubescore.ExpiryDate(childComplexity), true - case "OutdatedImage.currentImage": - if e.complexity.OutdatedImage.CurrentImage == nil { + case "Kubescore.fileName": + if e.complexity.Kubescore.FileName == nil { break } - return e.complexity.OutdatedImage.CurrentImage(childComplexity), true + return e.complexity.Kubescore.FileName(childComplexity), true - case "OutdatedImage.currentTag": - if e.complexity.OutdatedImage.CurrentTag == nil { + case "Kubescore.fileRow": + if e.complexity.Kubescore.FileRow == nil { break } - return e.complexity.OutdatedImage.CurrentTag(childComplexity), true + return e.complexity.Kubescore.FileRow(childComplexity), true - case "OutdatedImage.eventTime": - if e.complexity.OutdatedImage.EventTime == nil { + case "Kubescore.id": + if e.complexity.Kubescore.ID == nil { break } - return e.complexity.OutdatedImage.EventTime(childComplexity), true + return e.complexity.Kubescore.ID(childComplexity), true - case "OutdatedImage.latestVersion": - if e.complexity.OutdatedImage.LatestVersion == nil { + case "Kubescore.kind": + if e.complexity.Kubescore.Kind == nil { break } - return e.complexity.OutdatedImage.LatestVersion(childComplexity), true + return e.complexity.Kubescore.Kind(childComplexity), true - case "OutdatedImage.namespace": - if e.complexity.OutdatedImage.Namespace == nil { + case "Kubescore.name": + if e.complexity.Kubescore.Name == nil { break } - return e.complexity.OutdatedImage.Namespace(childComplexity), true + return e.complexity.Kubescore.Name(childComplexity), true - case "OutdatedImage.pod": - if e.complexity.OutdatedImage.Pod == nil { + case "Kubescore.namespace": + if e.complexity.Kubescore.Namespace == nil { break } - return e.complexity.OutdatedImage.Pod(childComplexity), true + return e.complexity.Kubescore.Namespace(childComplexity), true - case "OutdatedImage.versionsBehind": - if e.complexity.OutdatedImage.VersionsBehind == nil { + case "Kubescore.objectName": + if e.complexity.Kubescore.ObjectName == nil { break } - return e.complexity.OutdatedImage.VersionsBehind(childComplexity), true + return e.complexity.Kubescore.ObjectName(childComplexity), true - case "Query.allNamespaceData": - if e.complexity.Query.AllNamespaceData == nil { + case "Kubescore.path": + if e.complexity.Kubescore.Path == nil { break } - return e.complexity.Query.AllNamespaceData(childComplexity), true + return e.complexity.Kubescore.Path(childComplexity), true - case "Resource.age": - if e.complexity.Resource.Age == nil { + case "Kubescore.summary": + if e.complexity.Kubescore.Summary == nil { break } - return e.complexity.Resource.Age(childComplexity), true + return e.complexity.Kubescore.Summary(childComplexity), true - case "Resource.clusterName": - if e.complexity.Resource.ClusterName == nil { + case "Kubescore.targetType": + if e.complexity.Kubescore.TargetType == nil { break } - return e.complexity.Resource.ClusterName(childComplexity), true + return e.complexity.Kubescore.TargetType(childComplexity), true - case "Resource.eventTime": - if e.complexity.Resource.EventTime == nil { + case "NamespaceData.kubeScores": + if e.complexity.NamespaceData.KubeScores == nil { break } - return e.complexity.Resource.EventTime(childComplexity), true + return e.complexity.NamespaceData.KubeScores(childComplexity), true - case "Resource.kind": - if e.complexity.Resource.Kind == nil { + case "NamespaceData.namespace": + if e.complexity.NamespaceData.Namespace == nil { break } - return e.complexity.Resource.Kind(childComplexity), true + return e.complexity.NamespaceData.Namespace(childComplexity), true - case "Resource.namespace": - if e.complexity.Resource.Namespace == nil { + case "NamespaceData.outdatedImages": + if e.complexity.NamespaceData.OutdatedImages == nil { break } - return e.complexity.Resource.Namespace(childComplexity), true + return e.complexity.NamespaceData.OutdatedImages(childComplexity), true - case "Resource.resource": - if e.complexity.Resource.Resource == nil { + case "NamespaceData.resources": + if e.complexity.NamespaceData.Resources == nil { break } - return e.complexity.Resource.Resource(childComplexity), true + return e.complexity.NamespaceData.Resources(childComplexity), true - } - return 0, false -} + case "OutdatedImage.clusterName": + if e.complexity.OutdatedImage.ClusterName == nil { + break + } -func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { - rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} - inputUnmarshalMap := graphql.BuildUnmarshalerMap() - first := true + return e.complexity.OutdatedImage.ClusterName(childComplexity), true - switch rc.Operation.Operation { - case ast.Query: - return func(ctx context.Context) *graphql.Response { - var response graphql.Response - var data graphql.Marshaler - if first { - first = false - ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data = ec._Query(ctx, rc.Operation.SelectionSet) - } else { - if atomic.LoadInt32(&ec.pendingDeferred) > 0 { - result := <-ec.deferredResults - atomic.AddInt32(&ec.pendingDeferred, -1) - data = result.Result - response.Path = result.Path - response.Label = result.Label - response.Errors = result.Errors - } else { - return nil - } - } - var buf bytes.Buffer - data.MarshalGQL(&buf) - response.Data = buf.Bytes() - if atomic.LoadInt32(&ec.deferred) > 0 { - hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 - response.HasNext = &hasNext - } + case "OutdatedImage.currentImage": + if e.complexity.OutdatedImage.CurrentImage == nil { + break + } - return &response + return e.complexity.OutdatedImage.CurrentImage(childComplexity), true + + case "OutdatedImage.currentTag": + if e.complexity.OutdatedImage.CurrentTag == nil { + break } - default: - return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) - } -} + return e.complexity.OutdatedImage.CurrentTag(childComplexity), true -type executionContext struct { - *graphql.OperationContext - *executableSchema - deferred int32 - pendingDeferred int32 - deferredResults chan graphql.DeferredResult -} + case "OutdatedImage.eventTime": + if e.complexity.OutdatedImage.EventTime == nil { + break + } -func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { - atomic.AddInt32(&ec.pendingDeferred, 1) - go func() { - ctx := graphql.WithFreshResponseContext(dg.Context) - dg.FieldSet.Dispatch(ctx) - ds := graphql.DeferredResult{ - Path: dg.Path, - Label: dg.Label, - Result: dg.FieldSet, - Errors: graphql.GetErrors(ctx), + return e.complexity.OutdatedImage.EventTime(childComplexity), true + + case "OutdatedImage.latestVersion": + if e.complexity.OutdatedImage.LatestVersion == nil { + break } - // null fields should bubble up - if dg.FieldSet.Invalids > 0 { - ds.Result = graphql.Null + + return e.complexity.OutdatedImage.LatestVersion(childComplexity), true + + case "OutdatedImage.namespace": + if e.complexity.OutdatedImage.Namespace == nil { + break } - ec.deferredResults <- ds - }() -} -func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { - if ec.DisableIntrospection { - return nil, errors.New("introspection disabled") - } - return introspection.WrapSchema(ec.Schema()), nil -} + return e.complexity.OutdatedImage.Namespace(childComplexity), true -func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { - if ec.DisableIntrospection { - return nil, errors.New("introspection disabled") - } - return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil -} + case "OutdatedImage.pod": + if e.complexity.OutdatedImage.Pod == nil { + break + } -//go:embed "schema.graphqls" -var sourcesFS embed.FS + return e.complexity.OutdatedImage.Pod(childComplexity), true -func sourceData(filename string) string { - data, err := sourcesFS.ReadFile(filename) - if err != nil { - panic(fmt.Sprintf("codegen problem: %s not available", filename)) - } - return string(data) -} + case "OutdatedImage.versionsBehind": + if e.complexity.OutdatedImage.VersionsBehind == nil { + break + } -var sources = []*ast.Source{ - {Name: "schema.graphqls", Input: sourceData("schema.graphqls"), BuiltIn: false}, -} -var parsedSchema = gqlparser.MustLoadSchema(sources...) + return e.complexity.OutdatedImage.VersionsBehind(childComplexity), true -// endregion ************************** generated!.gotpl ************************** + case "Query.allDeletedAPIs": + if e.complexity.Query.AllDeletedAPIs == nil { + break + } -// region ***************************** args.gotpl ***************************** + return e.complexity.Query.AllDeletedAPIs(childComplexity), true -func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err + case "Query.allDeprecatedAPIs": + if e.complexity.Query.AllDeprecatedAPIs == nil { + break } - } - args["name"] = arg0 - return args, nil -} -func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) - if err != nil { - return nil, err + return e.complexity.Query.AllDeprecatedAPIs(childComplexity), true + + case "Query.allEvents": + if e.complexity.Query.AllEvents == nil { + break } - } - args["includeDeprecated"] = arg0 - return args, nil -} -func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { - var err error - args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) - if err != nil { - return nil, err + return e.complexity.Query.AllEvents(childComplexity), true + + case "Query.allGetAllResources": + if e.complexity.Query.AllGetAllResources == nil { + break } - } - args["includeDeprecated"] = arg0 - return args, nil -} -// endregion ***************************** args.gotpl ***************************** + return e.complexity.Query.AllGetAllResources(childComplexity), true -// region ************************** directives.gotpl ************************** + case "Query.allKubeScores": + if e.complexity.Query.AllKubeScores == nil { + break + } -// endregion ************************** directives.gotpl ************************** + return e.complexity.Query.AllKubeScores(childComplexity), true -// region **************************** field.gotpl ***************************** + case "Query.allNamespaceData": + if e.complexity.Query.AllNamespaceData == nil { + break + } -func (ec *executionContext) _KubeScore_id(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_id(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + return e.complexity.Query.AllNamespaceData(childComplexity), true + + case "Query.allRakkess": + if e.complexity.Query.AllRakkess == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ID, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Query.AllRakkess(childComplexity), true + + case "Query.allTrivyImages": + if e.complexity.Query.AllTrivyImages == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Query.AllTrivyImages(childComplexity), true -func (ec *executionContext) _KubeScore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_clusterName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Query.allTrivyMisconfigs": + if e.complexity.Query.AllTrivyMisconfigs == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Query.AllTrivyMisconfigs(childComplexity), true + + case "Query.allTrivySBOMs": + if e.complexity.Query.AllTrivySBOMs == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Query.AllTrivySBOMs(childComplexity), true -func (ec *executionContext) _KubeScore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_objectName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Query.allTrivyVuls": + if e.complexity.Query.AllTrivyVuls == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ObjectName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Query.AllTrivyVuls(childComplexity), true + + case "Rakkess.ClusterName": + if e.complexity.Rakkess.ClusterName == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Rakkess.ClusterName(childComplexity), true -func (ec *executionContext) _KubeScore_kind(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_kind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Rakkess.Create": + if e.complexity.Rakkess.Create == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Kind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Rakkess.Create(childComplexity), true + + case "Rakkess.Delete": + if e.complexity.Rakkess.Delete == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Rakkess.Delete(childComplexity), true -func (ec *executionContext) _KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_apiVersion(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Rakkess.EventTime": + if e.complexity.Rakkess.EventTime == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.APIVersion, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Rakkess.EventTime(childComplexity), true + + case "Rakkess.ExpiryDate": + if e.complexity.Rakkess.ExpiryDate == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Rakkess.ExpiryDate(childComplexity), true -func (ec *executionContext) _KubeScore_name(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_name(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Rakkess.List": + if e.complexity.Rakkess.List == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Name, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Rakkess.List(childComplexity), true + + case "Rakkess.Name": + if e.complexity.Rakkess.Name == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Rakkess.Name(childComplexity), true -func (ec *executionContext) _KubeScore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_namespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Rakkess.Update": + if e.complexity.Rakkess.Update == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Rakkess.Update(childComplexity), true + + case "Resource.age": + if e.complexity.Resource.Age == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Resource.Age(childComplexity), true -func (ec *executionContext) _KubeScore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_targetType(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Resource.clusterName": + if e.complexity.Resource.ClusterName == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.TargetType, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Resource.ClusterName(childComplexity), true + + case "Resource.eventTime": + if e.complexity.Resource.EventTime == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Resource.EventTime(childComplexity), true -func (ec *executionContext) _KubeScore_description(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_description(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Resource.kind": + if e.complexity.Resource.Kind == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Description, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Resource.Kind(childComplexity), true + + case "Resource.namespace": + if e.complexity.Resource.Namespace == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.Resource.Namespace(childComplexity), true -func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_path(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "Resource.resource": + if e.complexity.Resource.Resource == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Path, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.Resource.Resource(childComplexity), true + + case "TrivyImage.artifactName": + if e.complexity.TrivyImage.ArtifactName == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyImage.ArtifactName(childComplexity), true -func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_summary(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyImage.clusterName": + if e.complexity.TrivyImage.ClusterName == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Summary, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyImage.ClusterName(childComplexity), true + + case "TrivyImage.expiryDate": + if e.complexity.TrivyImage.ExpiryDate == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyImage.ExpiryDate(childComplexity), true -func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_fileName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyImage.id": + if e.complexity.TrivyImage.ID == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.FileName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyImage.ID(childComplexity), true + + case "TrivyImage.vulFixedVersion": + if e.complexity.TrivyImage.VulFixedVersion == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyImage.VulFixedVersion(childComplexity), true -func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_fileRow(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyImage.vulId": + if e.complexity.TrivyImage.VulID == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.FileRow, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyImage.VulID(childComplexity), true + + case "TrivyImage.vulInstalledVersion": + if e.complexity.TrivyImage.VulInstalledVersion == nil { + break } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyImage.VulInstalledVersion(childComplexity), true -func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_eventTime(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyImage.vulLastModifiedDate": + if e.complexity.TrivyImage.VulLastModifiedDate == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyImage.VulLastModifiedDate(childComplexity), true + + case "TrivyImage.vulPkgId": + if e.complexity.TrivyImage.VulPkgID == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_KubeScore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "KubeScore", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyImage.VulPkgID(childComplexity), true -func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_namespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyImage.vulPkgName": + if e.complexity.TrivyImage.VulPkgName == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyImage.VulPkgName(childComplexity), true + + case "TrivyImage.vulPublishedDate": + if e.complexity.TrivyImage.VulPublishedDate == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_NamespaceData_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "NamespaceData", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyImage.VulPublishedDate(childComplexity), true -func (ec *executionContext) _NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_outdatedImages(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyImage.vulSeverity": + if e.complexity.TrivyImage.VulSeverity == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.OutdatedImages, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyImage.VulSeverity(childComplexity), true + + case "TrivyImage.vulTitle": + if e.complexity.TrivyImage.VulTitle == nil { + break } - return graphql.Null - } - res := resTmp.([]*model.OutdatedImage) - fc.Result = res - return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "NamespaceData", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_OutdatedImage_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_OutdatedImage_namespace(ctx, field) - case "pod": - return ec.fieldContext_OutdatedImage_pod(ctx, field) - case "currentImage": - return ec.fieldContext_OutdatedImage_currentImage(ctx, field) - case "currentTag": - return ec.fieldContext_OutdatedImage_currentTag(ctx, field) - case "latestVersion": - return ec.fieldContext_OutdatedImage_latestVersion(ctx, field) - case "versionsBehind": - return ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) - case "eventTime": - return ec.fieldContext_OutdatedImage_eventTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) - }, - } - return fc, nil -} + return e.complexity.TrivyImage.VulTitle(childComplexity), true -func (ec *executionContext) _NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_kubeScores(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.clusterName": + if e.complexity.TrivyMisconfig.ClusterName == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.KubeScores, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.ClusterName(childComplexity), true + + case "TrivyMisconfig.eventTime": + if e.complexity.TrivyMisconfig.EventTime == nil { + break } - return graphql.Null - } - res := resTmp.([]*model.KubeScore) - fc.Result = res - return ec.marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "NamespaceData", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_KubeScore_id(ctx, field) - case "clusterName": - return ec.fieldContext_KubeScore_clusterName(ctx, field) - case "objectName": - return ec.fieldContext_KubeScore_objectName(ctx, field) - case "kind": - return ec.fieldContext_KubeScore_kind(ctx, field) - case "apiVersion": - return ec.fieldContext_KubeScore_apiVersion(ctx, field) - case "name": - return ec.fieldContext_KubeScore_name(ctx, field) - case "namespace": - return ec.fieldContext_KubeScore_namespace(ctx, field) - case "targetType": - return ec.fieldContext_KubeScore_targetType(ctx, field) - case "description": - return ec.fieldContext_KubeScore_description(ctx, field) - case "path": - return ec.fieldContext_KubeScore_path(ctx, field) - case "summary": - return ec.fieldContext_KubeScore_summary(ctx, field) - case "fileName": - return ec.fieldContext_KubeScore_fileName(ctx, field) - case "fileRow": - return ec.fieldContext_KubeScore_fileRow(ctx, field) - case "eventTime": - return ec.fieldContext_KubeScore_eventTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type KubeScore", field.Name) - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.EventTime(childComplexity), true -func (ec *executionContext) _NamespaceData_resources(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_resources(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.expiryDate": + if e.complexity.TrivyMisconfig.ExpiryDate == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Resources, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.ExpiryDate(childComplexity), true + + case "TrivyMisconfig.id": + if e.complexity.TrivyMisconfig.ID == nil { + break } - return graphql.Null - } - res := resTmp.([]*model.Resource) - fc.Result = res - return ec.marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_NamespaceData_resources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "NamespaceData", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_Resource_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_Resource_namespace(ctx, field) - case "kind": - return ec.fieldContext_Resource_kind(ctx, field) - case "resource": - return ec.fieldContext_Resource_resource(ctx, field) - case "age": - return ec.fieldContext_Resource_age(ctx, field) - case "eventTime": - return ec.fieldContext_Resource_eventTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Resource", field.Name) - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.ID(childComplexity), true -func (ec *executionContext) _OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_clusterName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.kind": + if e.complexity.TrivyMisconfig.Kind == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.Kind(childComplexity), true + + case "TrivyMisconfig.misconfigAvdid": + if e.complexity.TrivyMisconfig.MisconfigAvdid == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.MisconfigAvdid(childComplexity), true -func (ec *executionContext) _OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_namespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.misconfigDesc": + if e.complexity.TrivyMisconfig.MisconfigDesc == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.MisconfigDesc(childComplexity), true + + case "TrivyMisconfig.misconfigId": + if e.complexity.TrivyMisconfig.MisconfigID == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.MisconfigID(childComplexity), true -func (ec *executionContext) _OutdatedImage_pod(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_pod(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.misconfigMsg": + if e.complexity.TrivyMisconfig.MisconfigMsg == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Pod, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.MisconfigMsg(childComplexity), true + + case "TrivyMisconfig.misconfigQuery": + if e.complexity.TrivyMisconfig.MisconfigQuery == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_pod(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.MisconfigQuery(childComplexity), true -func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_currentImage(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.misconfigResolution": + if e.complexity.TrivyMisconfig.MisconfigResolution == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.CurrentImage, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.MisconfigResolution(childComplexity), true + + case "TrivyMisconfig.misconfigSeverity": + if e.complexity.TrivyMisconfig.MisconfigSeverity == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.MisconfigSeverity(childComplexity), true -func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_currentTag(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.misconfigStatus": + if e.complexity.TrivyMisconfig.MisconfigStatus == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.CurrentTag, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.MisconfigStatus(childComplexity), true + + case "TrivyMisconfig.misconfigTitle": + if e.complexity.TrivyMisconfig.MisconfigTitle == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.MisconfigTitle(childComplexity), true -func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_latestVersion(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.misconfigType": + if e.complexity.TrivyMisconfig.MisconfigType == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.LatestVersion, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.MisconfigType(childComplexity), true + + case "TrivyMisconfig.name": + if e.complexity.TrivyMisconfig.Name == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyMisconfig.Name(childComplexity), true -func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyMisconfig.namespace": + if e.complexity.TrivyMisconfig.Namespace == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.VersionsBehind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyMisconfig.Namespace(childComplexity), true + + case "TrivySBOM.bomFormat": + if e.complexity.TrivySBOM.BomFormat == nil { + break } - return graphql.Null - } - res := resTmp.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivySBOM.BomFormat(childComplexity), true -func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_eventTime(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivySBOM.bomRef": + if e.complexity.TrivySBOM.BomRef == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivySBOM.BomRef(childComplexity), true + + case "TrivySBOM.clusterName": + if e.complexity.TrivySBOM.ClusterName == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "OutdatedImage", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivySBOM.ClusterName(childComplexity), true -func (ec *executionContext) _Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allNamespaceData(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivySBOM.expiryDate": + if e.complexity.TrivySBOM.ExpiryDate == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllNamespaceData(rctx) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivySBOM.ExpiryDate(childComplexity), true + + case "TrivySBOM.id": + if e.complexity.TrivySBOM.ID == nil { + break } - return graphql.Null - } - res := resTmp.([]*model.NamespaceData) - fc.Result = res - return ec.marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Query", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "namespace": - return ec.fieldContext_NamespaceData_namespace(ctx, field) - case "outdatedImages": - return ec.fieldContext_NamespaceData_outdatedImages(ctx, field) - case "kubeScores": - return ec.fieldContext_NamespaceData_kubeScores(ctx, field) - case "resources": - return ec.fieldContext_NamespaceData_resources(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type NamespaceData", field.Name) - }, - } - return fc, nil -} + return e.complexity.TrivySBOM.ID(childComplexity), true -func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___type(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivySBOM.imageName": + if e.complexity.TrivySBOM.ImageName == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.introspectType(fc.Args["name"].(string)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*introspection.Type) - fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Query", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) + return e.complexity.TrivySBOM.ImageName(childComplexity), true + + case "TrivySBOM.packageName": + if e.complexity.TrivySBOM.PackageName == nil { + break } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} -func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___schema(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + return e.complexity.TrivySBOM.PackageName(childComplexity), true + + case "TrivySBOM.packageUrl": + if e.complexity.TrivySBOM.PackageURL == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.introspectSchema() - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*introspection.Schema) - fc.Result = res - return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Query", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "description": - return ec.fieldContext___Schema_description(ctx, field) - case "types": - return ec.fieldContext___Schema_types(ctx, field) - case "queryType": - return ec.fieldContext___Schema_queryType(ctx, field) - case "mutationType": - return ec.fieldContext___Schema_mutationType(ctx, field) - case "subscriptionType": - return ec.fieldContext___Schema_subscriptionType(ctx, field) - case "directives": - return ec.fieldContext___Schema_directives(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) - }, - } - return fc, nil -} + return e.complexity.TrivySBOM.PackageURL(childComplexity), true -func (ec *executionContext) _Resource_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_clusterName(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivySBOM.serialNumber": + if e.complexity.TrivySBOM.SerialNumber == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivySBOM.SerialNumber(childComplexity), true + + case "TrivySBOM.version": + if e.complexity.TrivySBOM.Version == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Resource_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Resource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivySBOM.Version(childComplexity), true -func (ec *executionContext) _Resource_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_namespace(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyVul.clusterName": + if e.complexity.TrivyVul.ClusterName == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyVul.ClusterName(childComplexity), true + + case "TrivyVul.expiryDate": + if e.complexity.TrivyVul.ExpiryDate == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Resource_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Resource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyVul.ExpiryDate(childComplexity), true -func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_kind(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyVul.id": + if e.complexity.TrivyVul.ID == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Kind, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyVul.ID(childComplexity), true + + case "TrivyVul.kind": + if e.complexity.TrivyVul.Kind == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Resource_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Resource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyVul.Kind(childComplexity), true -func (ec *executionContext) _Resource_resource(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_resource(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + case "TrivyVul.name": + if e.complexity.TrivyVul.Name == nil { + break } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Resource, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + + return e.complexity.TrivyVul.Name(childComplexity), true + + case "TrivyVul.namespace": + if e.complexity.TrivyVul.Namespace == nil { + break } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext_Resource_resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Resource", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} + return e.complexity.TrivyVul.Namespace(childComplexity), true -func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_age(ctx, field) + case "TrivyVul.vulFixedVersion": + if e.complexity.TrivyVul.VulFixedVersion == nil { + break + } + + return e.complexity.TrivyVul.VulFixedVersion(childComplexity), true + + case "TrivyVul.vulId": + if e.complexity.TrivyVul.VulID == nil { + break + } + + return e.complexity.TrivyVul.VulID(childComplexity), true + + case "TrivyVul.vulInstalledVersion": + if e.complexity.TrivyVul.VulInstalledVersion == nil { + break + } + + return e.complexity.TrivyVul.VulInstalledVersion(childComplexity), true + + case "TrivyVul.vulLastModifiedDate": + if e.complexity.TrivyVul.VulLastModifiedDate == nil { + break + } + + return e.complexity.TrivyVul.VulLastModifiedDate(childComplexity), true + + case "TrivyVul.vulPkgId": + if e.complexity.TrivyVul.VulPkgID == nil { + break + } + + return e.complexity.TrivyVul.VulPkgID(childComplexity), true + + case "TrivyVul.vulPkgName": + if e.complexity.TrivyVul.VulPkgName == nil { + break + } + + return e.complexity.TrivyVul.VulPkgName(childComplexity), true + + case "TrivyVul.vulPkgPath": + if e.complexity.TrivyVul.VulPkgPath == nil { + break + } + + return e.complexity.TrivyVul.VulPkgPath(childComplexity), true + + case "TrivyVul.vulPublishedDate": + if e.complexity.TrivyVul.VulPublishedDate == nil { + break + } + + return e.complexity.TrivyVul.VulPublishedDate(childComplexity), true + + case "TrivyVul.vulSeverity": + if e.complexity.TrivyVul.VulSeverity == nil { + break + } + + return e.complexity.TrivyVul.VulSeverity(childComplexity), true + + case "TrivyVul.vulTitle": + if e.complexity.TrivyVul.VulTitle == nil { + break + } + + return e.complexity.TrivyVul.VulTitle(childComplexity), true + + case "TrivyVul.vulVendorIds": + if e.complexity.TrivyVul.VulVendorIds == nil { + break + } + + return e.complexity.TrivyVul.VulVendorIds(childComplexity), true + + } + return 0, false +} + +func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { + rc := graphql.GetOperationContext(ctx) + ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} + inputUnmarshalMap := graphql.BuildUnmarshalerMap() + first := true + + switch rc.Operation.Operation { + case ast.Query: + return func(ctx context.Context) *graphql.Response { + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, rc.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.pendingDeferred) > 0 { + result := <-ec.deferredResults + atomic.AddInt32(&ec.pendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } + } + var buf bytes.Buffer + data.MarshalGQL(&buf) + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.pendingDeferred) > 0 + response.HasNext = &hasNext + } + + return &response + } + + default: + return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) + } +} + +type executionContext struct { + *graphql.OperationContext + *executableSchema + deferred int32 + pendingDeferred int32 + deferredResults chan graphql.DeferredResult +} + +func (ec *executionContext) processDeferredGroup(dg graphql.DeferredGroup) { + atomic.AddInt32(&ec.pendingDeferred, 1) + go func() { + ctx := graphql.WithFreshResponseContext(dg.Context) + dg.FieldSet.Dispatch(ctx) + ds := graphql.DeferredResult{ + Path: dg.Path, + Label: dg.Label, + Result: dg.FieldSet, + Errors: graphql.GetErrors(ctx), + } + // null fields should bubble up + if dg.FieldSet.Invalids > 0 { + ds.Result = graphql.Null + } + ec.deferredResults <- ds + }() +} + +func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapSchema(ec.Schema()), nil +} + +func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil +} + +//go:embed "schema.graphqls" +var sourcesFS embed.FS + +func sourceData(filename string) string { + data, err := sourcesFS.ReadFile(filename) + if err != nil { + panic(fmt.Sprintf("codegen problem: %s not available", filename)) + } + return string(data) +} + +var sources = []*ast.Source{ + {Name: "schema.graphqls", Input: sourceData("schema.graphqls"), BuiltIn: false}, +} +var parsedSchema = gqlparser.MustLoadSchema(sources...) + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["name"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -2089,26 +1596,23 @@ func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Age, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2119,8 +1623,8 @@ func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_eventTime(ctx, field) +func (ec *executionContext) _DeletedAPI_ObjectName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_ObjectName(ctx, field) if err != nil { return graphql.Null } @@ -2133,26 +1637,23 @@ func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.ObjectName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_ObjectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2163,8 +1664,8 @@ func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, return fc, nil } -func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_name(ctx, field) +func (ec *executionContext) _DeletedAPI_Group(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Group(ctx, field) if err != nil { return graphql.Null } @@ -2177,26 +1678,23 @@ func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Group, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Group(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Directive", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2207,8 +1705,8 @@ func (ec *executionContext) fieldContext___Directive_name(ctx context.Context, f return fc, nil } -func (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_description(ctx, field) +func (ec *executionContext) _DeletedAPI_Kind(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Kind(ctx, field) if err != nil { return graphql.Null } @@ -2221,7 +1719,7 @@ func (ec *executionContext) ___Directive_description(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description(), nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -2235,11 +1733,11 @@ func (ec *executionContext) ___Directive_description(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Directive", + Object: "DeletedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -2248,8 +1746,8 @@ func (ec *executionContext) fieldContext___Directive_description(ctx context.Con return fc, nil } -func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_locations(ctx, field) +func (ec *executionContext) _DeletedAPI_Version(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Version(ctx, field) if err != nil { return graphql.Null } @@ -2262,38 +1760,35 @@ func (ec *executionContext) ___Directive_locations(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Locations, nil + return obj.Version, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]string) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_locations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Directive", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type __DirectiveLocation does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_args(ctx, field) +func (ec *executionContext) _DeletedAPI_Name(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Name(ctx, field) if err != nil { return graphql.Null } @@ -2306,48 +1801,35 @@ func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Args, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]introspection.InputValue) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Directive", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext___InputValue_name(ctx, field) - case "description": - return ec.fieldContext___InputValue_description(ctx, field) - case "type": - return ec.fieldContext___InputValue_type(ctx, field) - case "defaultValue": - return ec.fieldContext___InputValue_defaultValue(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) +func (ec *executionContext) _DeletedAPI_Deleted(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Deleted(ctx, field) if err != nil { return graphql.Null } @@ -2360,26 +1842,23 @@ func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.IsRepeatable, nil + return obj.Deleted, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*bool) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Deleted(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Directive", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2390,8 +1869,8 @@ func (ec *executionContext) fieldContext___Directive_isRepeatable(ctx context.Co return fc, nil } -func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___EnumValue_name(ctx, field) +func (ec *executionContext) _DeletedAPI_Scope(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Scope(ctx, field) if err != nil { return graphql.Null } @@ -2404,26 +1883,23 @@ func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Scope, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Scope(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__EnumValue", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2434,8 +1910,8 @@ func (ec *executionContext) fieldContext___EnumValue_name(ctx context.Context, f return fc, nil } -func (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___EnumValue_description(ctx, field) +func (ec *executionContext) _DeletedAPI_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -2448,7 +1924,7 @@ func (ec *executionContext) ___EnumValue_description(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description(), nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -2462,11 +1938,11 @@ func (ec *executionContext) ___EnumValue_description(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__EnumValue", + Object: "DeletedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -2475,8 +1951,8 @@ func (ec *executionContext) fieldContext___EnumValue_description(ctx context.Con return fc, nil } -func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___EnumValue_isDeprecated(ctx, field) +func (ec *executionContext) _DeletedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) if err != nil { return graphql.Null } @@ -2489,38 +1965,35 @@ func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.IsDeprecated(), nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*string) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__EnumValue", + Object: "DeletedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___EnumValue_deprecationReason(ctx, field) +func (ec *executionContext) _DeprecatedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -2533,7 +2006,7 @@ func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.DeprecationReason(), nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -2547,11 +2020,11 @@ func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__EnumValue", + Object: "DeprecatedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -2560,8 +2033,8 @@ func (ec *executionContext) fieldContext___EnumValue_deprecationReason(ctx conte return fc, nil } -func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Field_name(ctx, field) +func (ec *executionContext) _DeprecatedAPI_ObjectName(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) if err != nil { return graphql.Null } @@ -2574,26 +2047,23 @@ func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.ObjectName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_ObjectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Field", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2604,8 +2074,8 @@ func (ec *executionContext) fieldContext___Field_name(ctx context.Context, field return fc, nil } -func (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Field_description(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Description(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Description(ctx, field) if err != nil { return graphql.Null } @@ -2618,7 +2088,7 @@ func (ec *executionContext) ___Field_description(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description(), nil + return obj.Description, nil }) if err != nil { ec.Error(ctx, err) @@ -2632,11 +2102,11 @@ func (ec *executionContext) ___Field_description(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Field", + Object: "DeprecatedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -2645,8 +2115,8 @@ func (ec *executionContext) fieldContext___Field_description(ctx context.Context return fc, nil } -func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Field_args(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Kind(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Kind(ctx, field) if err != nil { return graphql.Null } @@ -2659,48 +2129,35 @@ func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Args, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]introspection.InputValue) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Field", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext___InputValue_name(ctx, field) - case "description": - return ec.fieldContext___InputValue_description(ctx, field) - case "type": - return ec.fieldContext___InputValue_type(ctx, field) - case "defaultValue": - return ec.fieldContext___InputValue_defaultValue(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Field_type(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Deprecated(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) if err != nil { return graphql.Null } @@ -2713,60 +2170,35 @@ func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Type, nil + return obj.Deprecated, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.(*bool) fc.Result = res - return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Deprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Field", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Field_isDeprecated(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Scope(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Scope(ctx, field) if err != nil { return graphql.Null } @@ -2779,38 +2211,35 @@ func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.IsDeprecated(), nil + return obj.Scope, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*string) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Scope(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Field", + Object: "DeprecatedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Field_deprecationReason(ctx, field) +func (ec *executionContext) _DeprecatedAPI_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -2823,7 +2252,7 @@ func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.DeprecationReason(), nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -2837,11 +2266,11 @@ func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Field_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Field", + Object: "DeprecatedAPI", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -2850,8 +2279,8 @@ func (ec *executionContext) fieldContext___Field_deprecationReason(ctx context.C return fc, nil } -func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___InputValue_name(ctx, field) +func (ec *executionContext) _DeprecatedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) if err != nil { return graphql.Null } @@ -2864,26 +2293,23 @@ func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__InputValue", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2894,8 +2320,8 @@ func (ec *executionContext) fieldContext___InputValue_name(ctx context.Context, return fc, nil } -func (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___InputValue_description(ctx, field) +func (ec *executionContext) _Event_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -2908,7 +2334,7 @@ func (ec *executionContext) ___InputValue_description(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description(), nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -2922,11 +2348,11 @@ func (ec *executionContext) ___InputValue_description(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__InputValue", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -2935,8 +2361,8 @@ func (ec *executionContext) fieldContext___InputValue_description(ctx context.Co return fc, nil } -func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___InputValue_type(ctx, field) +func (ec *executionContext) _Event_Id(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Id(ctx, field) if err != nil { return graphql.Null } @@ -2949,60 +2375,35 @@ func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Type, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__InputValue", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___InputValue_defaultValue(ctx, field) +func (ec *executionContext) _Event_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -3015,7 +2416,7 @@ func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.DefaultValue, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -3029,9 +2430,9 @@ func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__InputValue", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3042,8 +2443,8 @@ func (ec *executionContext) fieldContext___InputValue_defaultValue(ctx context.C return fc, nil } -func (ec *executionContext) ___Schema_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Schema_description(ctx, field) +func (ec *executionContext) _Event_OpType(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_OpType(ctx, field) if err != nil { return graphql.Null } @@ -3056,7 +2457,7 @@ func (ec *executionContext) ___Schema_description(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description(), nil + return obj.OpType, nil }) if err != nil { ec.Error(ctx, err) @@ -3070,11 +2471,11 @@ func (ec *executionContext) ___Schema_description(ctx context.Context, field gra return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_OpType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Schema", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -3083,8 +2484,8 @@ func (ec *executionContext) fieldContext___Schema_description(ctx context.Contex return fc, nil } -func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Schema_types(ctx, field) +func (ec *executionContext) _Event_Name(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Name(ctx, field) if err != nil { return graphql.Null } @@ -3097,60 +2498,35 @@ func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Types(), nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_types(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Schema", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Schema_queryType(ctx, field) +func (ec *executionContext) _Event_Namespace(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Namespace(ctx, field) if err != nil { return graphql.Null } @@ -3163,60 +2539,35 @@ func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.QueryType(), nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_queryType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Schema", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Schema_mutationType(ctx, field) +func (ec *executionContext) _Event_Kind(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Kind(ctx, field) if err != nil { return graphql.Null } @@ -3229,7 +2580,7 @@ func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MutationType(), nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -3238,48 +2589,26 @@ func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field gr if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_mutationType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Schema", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Schema_subscriptionType(ctx, field) +func (ec *executionContext) _Event_Message(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Message(ctx, field) if err != nil { return graphql.Null } @@ -3292,7 +2621,7 @@ func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SubscriptionType(), nil + return obj.Message, nil }) if err != nil { ec.Error(ctx, err) @@ -3301,48 +2630,26 @@ func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, fiel if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Schema", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Schema_directives(ctx, field) +func (ec *executionContext) _Event_Reason(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Reason(ctx, field) if err != nil { return graphql.Null } @@ -3355,50 +2662,35 @@ func (ec *executionContext) ___Schema_directives(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Directives(), nil + return obj.Reason, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]introspection.Directive) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Schema_directives(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Reason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Schema", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext___Directive_name(ctx, field) - case "description": - return ec.fieldContext___Directive_description(ctx, field) - case "locations": - return ec.fieldContext___Directive_locations(ctx, field) - case "args": - return ec.fieldContext___Directive_args(ctx, field) - case "isRepeatable": - return ec.fieldContext___Directive_isRepeatable(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Directive", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_kind(ctx, field) +func (ec *executionContext) _Event_Host(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Host(ctx, field) if err != nil { return graphql.Null } @@ -3411,38 +2703,35 @@ func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind(), nil + return obj.Host, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalN__TypeKind2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Host(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type __TypeKind does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_name(ctx, field) +func (ec *executionContext) _Event_Event(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Event(ctx, field) if err != nil { return graphql.Null } @@ -3455,7 +2744,7 @@ func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name(), nil + return obj.Event, nil }) if err != nil { ec.Error(ctx, err) @@ -3469,11 +2758,11 @@ func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.Coll return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Event(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -3482,8 +2771,8 @@ func (ec *executionContext) fieldContext___Type_name(ctx context.Context, field return fc, nil } -func (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_description(ctx, field) +func (ec *executionContext) _Event_FirstTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_FirstTime(ctx, field) if err != nil { return graphql.Null } @@ -3496,7 +2785,7 @@ func (ec *executionContext) ___Type_description(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description(), nil + return obj.FirstTime, nil }) if err != nil { ec.Error(ctx, err) @@ -3510,11 +2799,11 @@ func (ec *executionContext) ___Type_description(ctx context.Context, field graph return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_FirstTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type String does not have child fields") @@ -3523,8 +2812,8 @@ func (ec *executionContext) fieldContext___Type_description(ctx context.Context, return fc, nil } -func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_fields(ctx, field) +func (ec *executionContext) _Event_LastTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_LastTime(ctx, field) if err != nil { return graphql.Null } @@ -3537,7 +2826,7 @@ func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Fields(fc.Args["includeDeprecated"].(bool)), nil + return obj.LastTime, nil }) if err != nil { ec.Error(ctx, err) @@ -3546,51 +2835,26 @@ func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.Co if resTmp == nil { return graphql.Null } - res := resTmp.([]introspection.Field) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_LastTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext___Field_name(ctx, field) - case "description": - return ec.fieldContext___Field_description(ctx, field) - case "args": - return ec.fieldContext___Field_args(ctx, field) - case "type": - return ec.fieldContext___Field_type(ctx, field) - case "isDeprecated": - return ec.fieldContext___Field_isDeprecated(ctx, field) - case "deprecationReason": - return ec.fieldContext___Field_deprecationReason(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Field", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_interfaces(ctx, field) +func (ec *executionContext) _Event_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_ExpiryDate(ctx, field) if err != nil { return graphql.Null } @@ -3603,7 +2867,7 @@ func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Interfaces(), nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) @@ -3612,48 +2876,26 @@ func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphq if resTmp == nil { return graphql.Null } - res := resTmp.([]introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_interfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "Event", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_possibleTypes(ctx, field) +func (ec *executionContext) _GetAllResource_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -3666,7 +2908,7 @@ func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.PossibleTypes(), nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -3675,48 +2917,26 @@ func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field gra if resTmp == nil { return graphql.Null } - res := resTmp.([]introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_possibleTypes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "GetAllResource", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_enumValues(ctx, field) +func (ec *executionContext) _GetAllResource_Namespace(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Namespace(ctx, field) if err != nil { return graphql.Null } @@ -3729,7 +2949,7 @@ func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EnumValues(fc.Args["includeDeprecated"].(bool)), nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) @@ -3738,293 +2958,9024 @@ func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphq if resTmp == nil { return graphql.Null } - res := resTmp.([]introspection.EnumValue) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_Namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "__Type", + Object: "GetAllResource", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext___EnumValue_name(ctx, field) - case "description": - return ec.fieldContext___EnumValue_description(ctx, field) - case "isDeprecated": - return ec.fieldContext___EnumValue_isDeprecated(ctx, field) - case "deprecationReason": - return ec.fieldContext___EnumValue_deprecationReason(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __EnumValue", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } + return fc, nil +} + +func (ec *executionContext) _GetAllResource_Kind(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) defer func() { if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GetAllResource_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GetAllResource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GetAllResource_Resource(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Resource(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null } }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Resource, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GetAllResource_Resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GetAllResource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GetAllResource_Age(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Age(ctx, field) + if err != nil { + return graphql.Null + } ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Age, nil + }) + if err != nil { ec.Error(ctx, err) - return fc, err + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GetAllResource_Age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GetAllResource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GetAllResource_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_EventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GetAllResource_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GetAllResource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, } return fc, nil } -func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_inputFields(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null +func (ec *executionContext) _GetAllResource_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GetAllResource_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GetAllResource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_id(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_objectName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ObjectName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_kind(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_apiVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.APIVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_name(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_targetType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.TargetType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_description(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_summary(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Summary, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_fileName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FileName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_fileRow(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FileRow, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_KubeScore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "KubeScore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_id(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_objectName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ObjectName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_kind(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_apiVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.APIVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_name(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_targetType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.TargetType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_description(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_path(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_path(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Path, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_summary(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_summary(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Summary, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_fileName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FileName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_fileRow(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.FileRow, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Kubescore_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_expiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Kubescore_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Kubescore", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_outdatedImages(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OutdatedImages, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.OutdatedImage) + fc.Result = res + return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_OutdatedImage_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_OutdatedImage_namespace(ctx, field) + case "pod": + return ec.fieldContext_OutdatedImage_pod(ctx, field) + case "currentImage": + return ec.fieldContext_OutdatedImage_currentImage(ctx, field) + case "currentTag": + return ec.fieldContext_OutdatedImage_currentTag(ctx, field) + case "latestVersion": + return ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + case "versionsBehind": + return ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + case "eventTime": + return ec.fieldContext_OutdatedImage_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_kubeScores(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.KubeScores, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.KubeScore) + fc.Result = res + return ec.marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_KubeScore_id(ctx, field) + case "clusterName": + return ec.fieldContext_KubeScore_clusterName(ctx, field) + case "objectName": + return ec.fieldContext_KubeScore_objectName(ctx, field) + case "kind": + return ec.fieldContext_KubeScore_kind(ctx, field) + case "apiVersion": + return ec.fieldContext_KubeScore_apiVersion(ctx, field) + case "name": + return ec.fieldContext_KubeScore_name(ctx, field) + case "namespace": + return ec.fieldContext_KubeScore_namespace(ctx, field) + case "targetType": + return ec.fieldContext_KubeScore_targetType(ctx, field) + case "description": + return ec.fieldContext_KubeScore_description(ctx, field) + case "path": + return ec.fieldContext_KubeScore_path(ctx, field) + case "summary": + return ec.fieldContext_KubeScore_summary(ctx, field) + case "fileName": + return ec.fieldContext_KubeScore_fileName(ctx, field) + case "fileRow": + return ec.fieldContext_KubeScore_fileRow(ctx, field) + case "eventTime": + return ec.fieldContext_KubeScore_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type KubeScore", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_resources(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_resources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Resources, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Resource) + fc.Result = res + return ec.marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_resources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_Resource_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_Resource_namespace(ctx, field) + case "kind": + return ec.fieldContext_Resource_kind(ctx, field) + case "resource": + return ec.fieldContext_Resource_resource(ctx, field) + case "age": + return ec.fieldContext_Resource_age(ctx, field) + case "eventTime": + return ec.fieldContext_Resource_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Resource", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_pod(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_pod(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Pod, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_pod(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_currentImage(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CurrentImage, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_currentTag(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CurrentTag, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.LatestVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VersionsBehind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allNamespaceData(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllNamespaceData(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.NamespaceData) + fc.Result = res + return ec.marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "namespace": + return ec.fieldContext_NamespaceData_namespace(ctx, field) + case "outdatedImages": + return ec.fieldContext_NamespaceData_outdatedImages(ctx, field) + case "kubeScores": + return ec.fieldContext_NamespaceData_kubeScores(ctx, field) + case "resources": + return ec.fieldContext_NamespaceData_resources(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type NamespaceData", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allEvents(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allEvents(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllEvents(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Event) + fc.Result = res + return ec.marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allEvents(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_Event_ClusterName(ctx, field) + case "Id": + return ec.fieldContext_Event_Id(ctx, field) + case "EventTime": + return ec.fieldContext_Event_EventTime(ctx, field) + case "OpType": + return ec.fieldContext_Event_OpType(ctx, field) + case "Name": + return ec.fieldContext_Event_Name(ctx, field) + case "Namespace": + return ec.fieldContext_Event_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_Event_Kind(ctx, field) + case "Message": + return ec.fieldContext_Event_Message(ctx, field) + case "Reason": + return ec.fieldContext_Event_Reason(ctx, field) + case "Host": + return ec.fieldContext_Event_Host(ctx, field) + case "Event": + return ec.fieldContext_Event_Event(ctx, field) + case "FirstTime": + return ec.fieldContext_Event_FirstTime(ctx, field) + case "LastTime": + return ec.fieldContext_Event_LastTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_Event_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Event", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allRakkess(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allRakkess(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllRakkess(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Rakkess) + fc.Result = res + return ec.marshalNRakkess2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐRakkessᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allRakkess(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_Rakkess_ClusterName(ctx, field) + case "Name": + return ec.fieldContext_Rakkess_Name(ctx, field) + case "Create": + return ec.fieldContext_Rakkess_Create(ctx, field) + case "Delete": + return ec.fieldContext_Rakkess_Delete(ctx, field) + case "List": + return ec.fieldContext_Rakkess_List(ctx, field) + case "Update": + return ec.fieldContext_Rakkess_Update(ctx, field) + case "EventTime": + return ec.fieldContext_Rakkess_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_Rakkess_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Rakkess", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allDeprecatedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allDeprecatedAPIs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllDeprecatedAPIs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.DeprecatedAPI) + fc.Result = res + return ec.marshalNDeprecatedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPIᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allDeprecatedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) + case "ObjectName": + return ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) + case "Description": + return ec.fieldContext_DeprecatedAPI_Description(ctx, field) + case "Kind": + return ec.fieldContext_DeprecatedAPI_Kind(ctx, field) + case "Deprecated": + return ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) + case "Scope": + return ec.fieldContext_DeprecatedAPI_Scope(ctx, field) + case "EventTime": + return ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeprecatedAPI", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allDeletedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allDeletedAPIs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllDeletedAPIs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.DeletedAPI) + fc.Result = res + return ec.marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allDeletedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_DeletedAPI_ClusterName(ctx, field) + case "ObjectName": + return ec.fieldContext_DeletedAPI_ObjectName(ctx, field) + case "Group": + return ec.fieldContext_DeletedAPI_Group(ctx, field) + case "Kind": + return ec.fieldContext_DeletedAPI_Kind(ctx, field) + case "Version": + return ec.fieldContext_DeletedAPI_Version(ctx, field) + case "Name": + return ec.fieldContext_DeletedAPI_Name(ctx, field) + case "Deleted": + return ec.fieldContext_DeletedAPI_Deleted(ctx, field) + case "Scope": + return ec.fieldContext_DeletedAPI_Scope(ctx, field) + case "EventTime": + return ec.fieldContext_DeletedAPI_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeletedAPI", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allGetAllResources(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allGetAllResources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllGetAllResources(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.GetAllResource) + fc.Result = res + return ec.marshalNGetAllResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allGetAllResources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_GetAllResource_ClusterName(ctx, field) + case "Namespace": + return ec.fieldContext_GetAllResource_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_GetAllResource_Kind(ctx, field) + case "Resource": + return ec.fieldContext_GetAllResource_Resource(ctx, field) + case "Age": + return ec.fieldContext_GetAllResource_Age(ctx, field) + case "EventTime": + return ec.fieldContext_GetAllResource_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GetAllResource", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivySBOMs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivySBOMs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivySBOMs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivySbom) + fc.Result = res + return ec.marshalNTrivySBOM2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbomᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivySBOMs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivySBOM_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivySBOM_clusterName(ctx, field) + case "imageName": + return ec.fieldContext_TrivySBOM_imageName(ctx, field) + case "packageName": + return ec.fieldContext_TrivySBOM_packageName(ctx, field) + case "packageUrl": + return ec.fieldContext_TrivySBOM_packageUrl(ctx, field) + case "bomRef": + return ec.fieldContext_TrivySBOM_bomRef(ctx, field) + case "serialNumber": + return ec.fieldContext_TrivySBOM_serialNumber(ctx, field) + case "version": + return ec.fieldContext_TrivySBOM_version(ctx, field) + case "bomFormat": + return ec.fieldContext_TrivySBOM_bomFormat(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivySBOM_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivySBOM", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivyImages(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivyImages(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivyImages(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyImage) + fc.Result = res + return ec.marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyImage_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyImage_clusterName(ctx, field) + case "artifactName": + return ec.fieldContext_TrivyImage_artifactName(ctx, field) + case "vulId": + return ec.fieldContext_TrivyImage_vulId(ctx, field) + case "vulPkgId": + return ec.fieldContext_TrivyImage_vulPkgId(ctx, field) + case "vulPkgName": + return ec.fieldContext_TrivyImage_vulPkgName(ctx, field) + case "vulInstalledVersion": + return ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) + case "vulFixedVersion": + return ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) + case "vulTitle": + return ec.fieldContext_TrivyImage_vulTitle(ctx, field) + case "vulSeverity": + return ec.fieldContext_TrivyImage_vulSeverity(ctx, field) + case "vulPublishedDate": + return ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) + case "vulLastModifiedDate": + return ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyImage_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyImage", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allKubeScores(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allKubeScores(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllKubeScores(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Kubescore) + fc.Result = res + return ec.marshalNKubescore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubescoreᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allKubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Kubescore_id(ctx, field) + case "clusterName": + return ec.fieldContext_Kubescore_clusterName(ctx, field) + case "objectName": + return ec.fieldContext_Kubescore_objectName(ctx, field) + case "kind": + return ec.fieldContext_Kubescore_kind(ctx, field) + case "apiVersion": + return ec.fieldContext_Kubescore_apiVersion(ctx, field) + case "name": + return ec.fieldContext_Kubescore_name(ctx, field) + case "namespace": + return ec.fieldContext_Kubescore_namespace(ctx, field) + case "targetType": + return ec.fieldContext_Kubescore_targetType(ctx, field) + case "description": + return ec.fieldContext_Kubescore_description(ctx, field) + case "path": + return ec.fieldContext_Kubescore_path(ctx, field) + case "summary": + return ec.fieldContext_Kubescore_summary(ctx, field) + case "fileName": + return ec.fieldContext_Kubescore_fileName(ctx, field) + case "fileRow": + return ec.fieldContext_Kubescore_fileRow(ctx, field) + case "eventTime": + return ec.fieldContext_Kubescore_eventTime(ctx, field) + case "expiryDate": + return ec.fieldContext_Kubescore_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Kubescore", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivyVuls(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivyVuls(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivyVuls(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyVul) + fc.Result = res + return ec.marshalNTrivyVul2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyVulᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyVuls(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyVul_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyVul_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_TrivyVul_namespace(ctx, field) + case "kind": + return ec.fieldContext_TrivyVul_kind(ctx, field) + case "name": + return ec.fieldContext_TrivyVul_name(ctx, field) + case "vulId": + return ec.fieldContext_TrivyVul_vulId(ctx, field) + case "vulVendorIds": + return ec.fieldContext_TrivyVul_vulVendorIds(ctx, field) + case "vulPkgId": + return ec.fieldContext_TrivyVul_vulPkgId(ctx, field) + case "vulPkgName": + return ec.fieldContext_TrivyVul_vulPkgName(ctx, field) + case "vulPkgPath": + return ec.fieldContext_TrivyVul_vulPkgPath(ctx, field) + case "vulInstalledVersion": + return ec.fieldContext_TrivyVul_vulInstalledVersion(ctx, field) + case "vulFixedVersion": + return ec.fieldContext_TrivyVul_vulFixedVersion(ctx, field) + case "vulTitle": + return ec.fieldContext_TrivyVul_vulTitle(ctx, field) + case "vulSeverity": + return ec.fieldContext_TrivyVul_vulSeverity(ctx, field) + case "vulPublishedDate": + return ec.fieldContext_TrivyVul_vulPublishedDate(ctx, field) + case "vulLastModifiedDate": + return ec.fieldContext_TrivyVul_vulLastModifiedDate(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyVul_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyVul", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivyMisconfigs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivyMisconfigs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyMisconfig) + fc.Result = res + return ec.marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyMisconfig_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_TrivyMisconfig_namespace(ctx, field) + case "kind": + return ec.fieldContext_TrivyMisconfig_kind(ctx, field) + case "name": + return ec.fieldContext_TrivyMisconfig_name(ctx, field) + case "misconfigId": + return ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) + case "misconfigAvdid": + return ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) + case "misconfigType": + return ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) + case "misconfigTitle": + return ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) + case "misconfigDesc": + return ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) + case "misconfigMsg": + return ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) + case "misconfigQuery": + return ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) + case "misconfigResolution": + return ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) + case "misconfigSeverity": + return ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) + case "misconfigStatus": + return ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) + case "eventTime": + return ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyMisconfig", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(fc.Args["name"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___schema(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + fc.Result = res + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "description": + return ec.fieldContext___Schema_description(ctx, field) + case "types": + return ec.fieldContext___Schema_types(ctx, field) + case "queryType": + return ec.fieldContext___Schema_queryType(ctx, field) + case "mutationType": + return ec.fieldContext___Schema_mutationType(ctx, field) + case "subscriptionType": + return ec.fieldContext___Schema_subscriptionType(ctx, field) + case "directives": + return ec.fieldContext___Schema_directives(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_ClusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Name(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Create(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Create(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Create, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Create(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Delete(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Delete(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Delete, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Delete(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_List(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_List(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.List, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_List(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Update(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Update(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Update, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Update(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_EventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_ExpiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_resource(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_resource(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Resource, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_age(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Age, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_artifactName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_artifactName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ArtifactName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_artifactName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulPkgId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPkgID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulPkgName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPkgName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulInstalledVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulFixedVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulTitle(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulTitle, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulSeverity(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulSeverity, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPublishedDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulLastModifiedDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_expiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_namespace(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_kind(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_name(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigAvdid(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigAvdid, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigAvdid(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigType(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigTitle, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigDesc(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigDesc, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigDesc(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigMsg(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigMsg, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigMsg(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigQuery(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigQuery, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigQuery(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigResolution(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigResolution, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigResolution(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigSeverity, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_misconfigStatus(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MisconfigStatus, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigStatus(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyMisconfig_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyMisconfig_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyMisconfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_imageName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_imageName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ImageName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_imageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_packageName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_packageName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PackageName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_packageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_packageUrl(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_packageUrl(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PackageURL, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_packageUrl(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_bomRef(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_bomRef(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.BomRef, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_bomRef(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_serialNumber(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_serialNumber(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SerialNumber, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_serialNumber(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_version(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_version(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Version, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_bomFormat(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_bomFormat(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.BomFormat, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_bomFormat(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_expiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_namespace(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_kind(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_name(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulVendorIds(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulVendorIds(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulVendorIds, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulVendorIds(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPkgId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPkgID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPkgName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPkgName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulPkgPath(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPkgPath(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPkgPath, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulPkgPath(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulInstalledVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulInstalledVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulFixedVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulFixedVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulTitle(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulTitle, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulSeverity(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulSeverity, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPublishedDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulPublishedDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulLastModifiedDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VulLastModifiedDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivyVul_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_expiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyVul_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyVul", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_locations(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Locations, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_locations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type __DirectiveLocation does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_args(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Directive_isRepeatable(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsRepeatable, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_isDeprecated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___EnumValue_deprecationReason(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__EnumValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_args(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_isDeprecated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_isDeprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Field_deprecationReason(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Field_deprecationReason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___InputValue_defaultValue(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DefaultValue, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_types(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Types(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_types(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_queryType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.QueryType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_queryType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_mutationType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MutationType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_mutationType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_subscriptionType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SubscriptionType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Schema_directives(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Directives(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.Directive) + fc.Result = res + return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Schema_directives(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___Directive_name(ctx, field) + case "description": + return ec.fieldContext___Directive_description(ctx, field) + case "locations": + return ec.fieldContext___Directive_locations(ctx, field) + case "args": + return ec.fieldContext___Directive_args(ctx, field) + case "isRepeatable": + return ec.fieldContext___Directive_isRepeatable(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Directive", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_kind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalN__TypeKind2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type __TypeKind does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_description(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_fields(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Fields(fc.Args["includeDeprecated"].(bool)), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Field) + fc.Result = res + return ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___Field_name(ctx, field) + case "description": + return ec.fieldContext___Field_description(ctx, field) + case "args": + return ec.fieldContext___Field_args(ctx, field) + case "type": + return ec.fieldContext___Field_type(ctx, field) + case "isDeprecated": + return ec.fieldContext___Field_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___Field_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Field", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_interfaces(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Interfaces(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_interfaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_possibleTypes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PossibleTypes(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_possibleTypes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_enumValues(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EnumValues(fc.Args["includeDeprecated"].(bool)), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.EnumValue) + fc.Result = res + return ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___EnumValue_name(ctx, field) + case "description": + return ec.fieldContext___EnumValue_description(ctx, field) + case "isDeprecated": + return ec.fieldContext___EnumValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___EnumValue_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __EnumValue", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_inputFields(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.InputFields(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + fc.Result = res + return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_inputFields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_ofType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OfType(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_ofType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SpecifiedByURL(), nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var deletedAPIImplementors = []string{"DeletedAPI"} + +func (ec *executionContext) _DeletedAPI(ctx context.Context, sel ast.SelectionSet, obj *model.DeletedAPI) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deletedAPIImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeletedAPI") + case "ClusterName": + out.Values[i] = ec._DeletedAPI_ClusterName(ctx, field, obj) + case "ObjectName": + out.Values[i] = ec._DeletedAPI_ObjectName(ctx, field, obj) + case "Group": + out.Values[i] = ec._DeletedAPI_Group(ctx, field, obj) + case "Kind": + out.Values[i] = ec._DeletedAPI_Kind(ctx, field, obj) + case "Version": + out.Values[i] = ec._DeletedAPI_Version(ctx, field, obj) + case "Name": + out.Values[i] = ec._DeletedAPI_Name(ctx, field, obj) + case "Deleted": + out.Values[i] = ec._DeletedAPI_Deleted(ctx, field, obj) + case "Scope": + out.Values[i] = ec._DeletedAPI_Scope(ctx, field, obj) + case "EventTime": + out.Values[i] = ec._DeletedAPI_EventTime(ctx, field, obj) + case "ExpiryDate": + out.Values[i] = ec._DeletedAPI_ExpiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var deprecatedAPIImplementors = []string{"DeprecatedAPI"} + +func (ec *executionContext) _DeprecatedAPI(ctx context.Context, sel ast.SelectionSet, obj *model.DeprecatedAPI) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deprecatedAPIImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeprecatedAPI") + case "ClusterName": + out.Values[i] = ec._DeprecatedAPI_ClusterName(ctx, field, obj) + case "ObjectName": + out.Values[i] = ec._DeprecatedAPI_ObjectName(ctx, field, obj) + case "Description": + out.Values[i] = ec._DeprecatedAPI_Description(ctx, field, obj) + case "Kind": + out.Values[i] = ec._DeprecatedAPI_Kind(ctx, field, obj) + case "Deprecated": + out.Values[i] = ec._DeprecatedAPI_Deprecated(ctx, field, obj) + case "Scope": + out.Values[i] = ec._DeprecatedAPI_Scope(ctx, field, obj) + case "EventTime": + out.Values[i] = ec._DeprecatedAPI_EventTime(ctx, field, obj) + case "ExpiryDate": + out.Values[i] = ec._DeprecatedAPI_ExpiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var eventImplementors = []string{"Event"} + +func (ec *executionContext) _Event(ctx context.Context, sel ast.SelectionSet, obj *model.Event) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, eventImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Event") + case "ClusterName": + out.Values[i] = ec._Event_ClusterName(ctx, field, obj) + case "Id": + out.Values[i] = ec._Event_Id(ctx, field, obj) + case "EventTime": + out.Values[i] = ec._Event_EventTime(ctx, field, obj) + case "OpType": + out.Values[i] = ec._Event_OpType(ctx, field, obj) + case "Name": + out.Values[i] = ec._Event_Name(ctx, field, obj) + case "Namespace": + out.Values[i] = ec._Event_Namespace(ctx, field, obj) + case "Kind": + out.Values[i] = ec._Event_Kind(ctx, field, obj) + case "Message": + out.Values[i] = ec._Event_Message(ctx, field, obj) + case "Reason": + out.Values[i] = ec._Event_Reason(ctx, field, obj) + case "Host": + out.Values[i] = ec._Event_Host(ctx, field, obj) + case "Event": + out.Values[i] = ec._Event_Event(ctx, field, obj) + case "FirstTime": + out.Values[i] = ec._Event_FirstTime(ctx, field, obj) + case "LastTime": + out.Values[i] = ec._Event_LastTime(ctx, field, obj) + case "ExpiryDate": + out.Values[i] = ec._Event_ExpiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var getAllResourceImplementors = []string{"GetAllResource"} + +func (ec *executionContext) _GetAllResource(ctx context.Context, sel ast.SelectionSet, obj *model.GetAllResource) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, getAllResourceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GetAllResource") + case "ClusterName": + out.Values[i] = ec._GetAllResource_ClusterName(ctx, field, obj) + case "Namespace": + out.Values[i] = ec._GetAllResource_Namespace(ctx, field, obj) + case "Kind": + out.Values[i] = ec._GetAllResource_Kind(ctx, field, obj) + case "Resource": + out.Values[i] = ec._GetAllResource_Resource(ctx, field, obj) + case "Age": + out.Values[i] = ec._GetAllResource_Age(ctx, field, obj) + case "EventTime": + out.Values[i] = ec._GetAllResource_EventTime(ctx, field, obj) + case "ExpiryDate": + out.Values[i] = ec._GetAllResource_ExpiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var kubeScoreImplementors = []string{"KubeScore"} + +func (ec *executionContext) _KubeScore(ctx context.Context, sel ast.SelectionSet, obj *model.KubeScore) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, kubeScoreImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("KubeScore") + case "id": + out.Values[i] = ec._KubeScore_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._KubeScore_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "objectName": + out.Values[i] = ec._KubeScore_objectName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._KubeScore_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "apiVersion": + out.Values[i] = ec._KubeScore_apiVersion(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._KubeScore_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._KubeScore_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "targetType": + out.Values[i] = ec._KubeScore_targetType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec._KubeScore_description(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "path": + out.Values[i] = ec._KubeScore_path(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "summary": + out.Values[i] = ec._KubeScore_summary(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "fileName": + out.Values[i] = ec._KubeScore_fileName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "fileRow": + out.Values[i] = ec._KubeScore_fileRow(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventTime": + out.Values[i] = ec._KubeScore_eventTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var kubescoreImplementors = []string{"Kubescore"} + +func (ec *executionContext) _Kubescore(ctx context.Context, sel ast.SelectionSet, obj *model.Kubescore) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, kubescoreImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Kubescore") + case "id": + out.Values[i] = ec._Kubescore_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._Kubescore_clusterName(ctx, field, obj) + case "objectName": + out.Values[i] = ec._Kubescore_objectName(ctx, field, obj) + case "kind": + out.Values[i] = ec._Kubescore_kind(ctx, field, obj) + case "apiVersion": + out.Values[i] = ec._Kubescore_apiVersion(ctx, field, obj) + case "name": + out.Values[i] = ec._Kubescore_name(ctx, field, obj) + case "namespace": + out.Values[i] = ec._Kubescore_namespace(ctx, field, obj) + case "targetType": + out.Values[i] = ec._Kubescore_targetType(ctx, field, obj) + case "description": + out.Values[i] = ec._Kubescore_description(ctx, field, obj) + case "path": + out.Values[i] = ec._Kubescore_path(ctx, field, obj) + case "summary": + out.Values[i] = ec._Kubescore_summary(ctx, field, obj) + case "fileName": + out.Values[i] = ec._Kubescore_fileName(ctx, field, obj) + case "fileRow": + out.Values[i] = ec._Kubescore_fileRow(ctx, field, obj) + case "eventTime": + out.Values[i] = ec._Kubescore_eventTime(ctx, field, obj) + case "expiryDate": + out.Values[i] = ec._Kubescore_expiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var namespaceDataImplementors = []string{"NamespaceData"} + +func (ec *executionContext) _NamespaceData(ctx context.Context, sel ast.SelectionSet, obj *model.NamespaceData) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, namespaceDataImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("NamespaceData") + case "namespace": + out.Values[i] = ec._NamespaceData_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "outdatedImages": + out.Values[i] = ec._NamespaceData_outdatedImages(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kubeScores": + out.Values[i] = ec._NamespaceData_kubeScores(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resources": + out.Values[i] = ec._NamespaceData_resources(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var outdatedImageImplementors = []string{"OutdatedImage"} + +func (ec *executionContext) _OutdatedImage(ctx context.Context, sel ast.SelectionSet, obj *model.OutdatedImage) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, outdatedImageImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("OutdatedImage") + case "clusterName": + out.Values[i] = ec._OutdatedImage_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._OutdatedImage_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pod": + out.Values[i] = ec._OutdatedImage_pod(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "currentImage": + out.Values[i] = ec._OutdatedImage_currentImage(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "currentTag": + out.Values[i] = ec._OutdatedImage_currentTag(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "latestVersion": + out.Values[i] = ec._OutdatedImage_latestVersion(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "versionsBehind": + out.Values[i] = ec._OutdatedImage_versionsBehind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventTime": + out.Values[i] = ec._OutdatedImage_eventTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var queryImplementors = []string{"Query"} + +func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, queryImplementors) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Query", + }) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ + Object: field.Name, + Field: field, + }) + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Query") + case "allNamespaceData": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allNamespaceData(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allEvents": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allEvents(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allRakkess": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allRakkess(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allDeprecatedAPIs": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allDeprecatedAPIs(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allDeletedAPIs": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allDeletedAPIs(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allGetAllResources": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allGetAllResources(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allTrivySBOMs": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allTrivySBOMs(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allTrivyImages": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allTrivyImages(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allKubeScores": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allKubeScores(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allTrivyVuls": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allTrivyVuls(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allTrivyMisconfigs": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allTrivyMisconfigs(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "__type": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Query___type(ctx, field) + }) + case "__schema": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Query___schema(ctx, field) + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var rakkessImplementors = []string{"Rakkess"} + +func (ec *executionContext) _Rakkess(ctx context.Context, sel ast.SelectionSet, obj *model.Rakkess) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, rakkessImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Rakkess") + case "ClusterName": + out.Values[i] = ec._Rakkess_ClusterName(ctx, field, obj) + case "Name": + out.Values[i] = ec._Rakkess_Name(ctx, field, obj) + case "Create": + out.Values[i] = ec._Rakkess_Create(ctx, field, obj) + case "Delete": + out.Values[i] = ec._Rakkess_Delete(ctx, field, obj) + case "List": + out.Values[i] = ec._Rakkess_List(ctx, field, obj) + case "Update": + out.Values[i] = ec._Rakkess_Update(ctx, field, obj) + case "EventTime": + out.Values[i] = ec._Rakkess_EventTime(ctx, field, obj) + case "ExpiryDate": + out.Values[i] = ec._Rakkess_ExpiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.InputFields(), nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null } - if resTmp == nil { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } - res := resTmp.([]introspection.InputValue) - fc.Result = res - return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext___Type_inputFields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Type", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext___InputValue_name(ctx, field) - case "description": - return ec.fieldContext___InputValue_description(ctx, field) - case "type": - return ec.fieldContext___InputValue_type(ctx, field) - case "defaultValue": - return ec.fieldContext___InputValue_defaultValue(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) - }, + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) } - return fc, nil + + return out } -func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_ofType(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null +var resourceImplementors = []string{"Resource"} + +func (ec *executionContext) _Resource(ctx context.Context, sel ast.SelectionSet, obj *model.Resource) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, resourceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Resource") + case "clusterName": + out.Values[i] = ec._Resource_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._Resource_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._Resource_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resource": + out.Values[i] = ec._Resource_resource(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "age": + out.Values[i] = ec._Resource_age(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "eventTime": + out.Values[i] = ec._Resource_eventTime(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.OfType(), nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null } - if resTmp == nil { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } - res := resTmp.(*introspection.Type) - fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext___Type_ofType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Type", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) - }, + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) } - return fc, nil + + return out } -func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { - fc, err := ec.fieldContext___Type_specifiedByURL(ctx, field) - if err != nil { +var trivyImageImplementors = []string{"TrivyImage"} + +func (ec *executionContext) _TrivyImage(ctx context.Context, sel ast.SelectionSet, obj *model.TrivyImage) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, trivyImageImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TrivyImage") + case "id": + out.Values[i] = ec._TrivyImage_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._TrivyImage_clusterName(ctx, field, obj) + case "artifactName": + out.Values[i] = ec._TrivyImage_artifactName(ctx, field, obj) + case "vulId": + out.Values[i] = ec._TrivyImage_vulId(ctx, field, obj) + case "vulPkgId": + out.Values[i] = ec._TrivyImage_vulPkgId(ctx, field, obj) + case "vulPkgName": + out.Values[i] = ec._TrivyImage_vulPkgName(ctx, field, obj) + case "vulInstalledVersion": + out.Values[i] = ec._TrivyImage_vulInstalledVersion(ctx, field, obj) + case "vulFixedVersion": + out.Values[i] = ec._TrivyImage_vulFixedVersion(ctx, field, obj) + case "vulTitle": + out.Values[i] = ec._TrivyImage_vulTitle(ctx, field, obj) + case "vulSeverity": + out.Values[i] = ec._TrivyImage_vulSeverity(ctx, field, obj) + case "vulPublishedDate": + out.Values[i] = ec._TrivyImage_vulPublishedDate(ctx, field, obj) + case "vulLastModifiedDate": + out.Values[i] = ec._TrivyImage_vulLastModifiedDate(ctx, field, obj) + case "expiryDate": + out.Values[i] = ec._TrivyImage_expiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var trivyMisconfigImplementors = []string{"TrivyMisconfig"} + +func (ec *executionContext) _TrivyMisconfig(ctx context.Context, sel ast.SelectionSet, obj *model.TrivyMisconfig) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, trivyMisconfigImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TrivyMisconfig") + case "id": + out.Values[i] = ec._TrivyMisconfig_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._TrivyMisconfig_clusterName(ctx, field, obj) + case "namespace": + out.Values[i] = ec._TrivyMisconfig_namespace(ctx, field, obj) + case "kind": + out.Values[i] = ec._TrivyMisconfig_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._TrivyMisconfig_name(ctx, field, obj) + case "misconfigId": + out.Values[i] = ec._TrivyMisconfig_misconfigId(ctx, field, obj) + case "misconfigAvdid": + out.Values[i] = ec._TrivyMisconfig_misconfigAvdid(ctx, field, obj) + case "misconfigType": + out.Values[i] = ec._TrivyMisconfig_misconfigType(ctx, field, obj) + case "misconfigTitle": + out.Values[i] = ec._TrivyMisconfig_misconfigTitle(ctx, field, obj) + case "misconfigDesc": + out.Values[i] = ec._TrivyMisconfig_misconfigDesc(ctx, field, obj) + case "misconfigMsg": + out.Values[i] = ec._TrivyMisconfig_misconfigMsg(ctx, field, obj) + case "misconfigQuery": + out.Values[i] = ec._TrivyMisconfig_misconfigQuery(ctx, field, obj) + case "misconfigResolution": + out.Values[i] = ec._TrivyMisconfig_misconfigResolution(ctx, field, obj) + case "misconfigSeverity": + out.Values[i] = ec._TrivyMisconfig_misconfigSeverity(ctx, field, obj) + case "misconfigStatus": + out.Values[i] = ec._TrivyMisconfig_misconfigStatus(ctx, field, obj) + case "eventTime": + out.Values[i] = ec._TrivyMisconfig_eventTime(ctx, field, obj) + case "expiryDate": + out.Values[i] = ec._TrivyMisconfig_expiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.SpecifiedByURL(), nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null } - if resTmp == nil { + out.Dispatch(ctx) + if out.Invalids > 0 { return graphql.Null } - res := resTmp.(*string) - fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) -} -func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Type", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out } -// endregion **************************** field.gotpl ***************************** +var trivySBOMImplementors = []string{"TrivySBOM"} -// region **************************** input.gotpl ***************************** +func (ec *executionContext) _TrivySBOM(ctx context.Context, sel ast.SelectionSet, obj *model.TrivySbom) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, trivySBOMImplementors) -// endregion **************************** input.gotpl ***************************** + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TrivySBOM") + case "id": + out.Values[i] = ec._TrivySBOM_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._TrivySBOM_clusterName(ctx, field, obj) + case "imageName": + out.Values[i] = ec._TrivySBOM_imageName(ctx, field, obj) + case "packageName": + out.Values[i] = ec._TrivySBOM_packageName(ctx, field, obj) + case "packageUrl": + out.Values[i] = ec._TrivySBOM_packageUrl(ctx, field, obj) + case "bomRef": + out.Values[i] = ec._TrivySBOM_bomRef(ctx, field, obj) + case "serialNumber": + out.Values[i] = ec._TrivySBOM_serialNumber(ctx, field, obj) + case "version": + out.Values[i] = ec._TrivySBOM_version(ctx, field, obj) + case "bomFormat": + out.Values[i] = ec._TrivySBOM_bomFormat(ctx, field, obj) + case "expiryDate": + out.Values[i] = ec._TrivySBOM_expiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } -// region ************************** interface.gotpl *************************** + atomic.AddInt32(&ec.deferred, int32(len(deferred))) -// endregion ************************** interface.gotpl *************************** + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } -// region **************************** object.gotpl **************************** + return out +} -var kubeScoreImplementors = []string{"KubeScore"} +var trivyVulImplementors = []string{"TrivyVul"} -func (ec *executionContext) _KubeScore(ctx context.Context, sel ast.SelectionSet, obj *model.KubeScore) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, kubeScoreImplementors) +func (ec *executionContext) _TrivyVul(ctx context.Context, sel ast.SelectionSet, obj *model.TrivyVul) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, trivyVulImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("KubeScore") + out.Values[i] = graphql.MarshalString("TrivyVul") case "id": - out.Values[i] = ec._KubeScore_id(ctx, field, obj) + out.Values[i] = ec._TrivyVul_id(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "clusterName": - out.Values[i] = ec._KubeScore_clusterName(ctx, field, obj) + out.Values[i] = ec._TrivyVul_clusterName(ctx, field, obj) + case "namespace": + out.Values[i] = ec._TrivyVul_namespace(ctx, field, obj) + case "kind": + out.Values[i] = ec._TrivyVul_kind(ctx, field, obj) + case "name": + out.Values[i] = ec._TrivyVul_name(ctx, field, obj) + case "vulId": + out.Values[i] = ec._TrivyVul_vulId(ctx, field, obj) + case "vulVendorIds": + out.Values[i] = ec._TrivyVul_vulVendorIds(ctx, field, obj) + case "vulPkgId": + out.Values[i] = ec._TrivyVul_vulPkgId(ctx, field, obj) + case "vulPkgName": + out.Values[i] = ec._TrivyVul_vulPkgName(ctx, field, obj) + case "vulPkgPath": + out.Values[i] = ec._TrivyVul_vulPkgPath(ctx, field, obj) + case "vulInstalledVersion": + out.Values[i] = ec._TrivyVul_vulInstalledVersion(ctx, field, obj) + case "vulFixedVersion": + out.Values[i] = ec._TrivyVul_vulFixedVersion(ctx, field, obj) + case "vulTitle": + out.Values[i] = ec._TrivyVul_vulTitle(ctx, field, obj) + case "vulSeverity": + out.Values[i] = ec._TrivyVul_vulSeverity(ctx, field, obj) + case "vulPublishedDate": + out.Values[i] = ec._TrivyVul_vulPublishedDate(ctx, field, obj) + case "vulLastModifiedDate": + out.Values[i] = ec._TrivyVul_vulLastModifiedDate(ctx, field, obj) + case "expiryDate": + out.Values[i] = ec._TrivyVul_expiryDate(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __DirectiveImplementors = []string{"__Directive"} + +func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Directive") + case "name": + out.Values[i] = ec.___Directive_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "objectName": - out.Values[i] = ec._KubeScore_objectName(ctx, field, obj) + case "description": + out.Values[i] = ec.___Directive_description(ctx, field, obj) + case "locations": + out.Values[i] = ec.___Directive_locations(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "kind": - out.Values[i] = ec._KubeScore_kind(ctx, field, obj) + case "args": + out.Values[i] = ec.___Directive_args(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "apiVersion": - out.Values[i] = ec._KubeScore_apiVersion(ctx, field, obj) + case "isRepeatable": + out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __EnumValueImplementors = []string{"__EnumValue"} + +func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__EnumValue") case "name": - out.Values[i] = ec._KubeScore_name(ctx, field, obj) + out.Values[i] = ec.___EnumValue_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "namespace": - out.Values[i] = ec._KubeScore_namespace(ctx, field, obj) + case "description": + out.Values[i] = ec.___EnumValue_description(ctx, field, obj) + case "isDeprecated": + out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "targetType": - out.Values[i] = ec._KubeScore_targetType(ctx, field, obj) + case "deprecationReason": + out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __FieldImplementors = []string{"__Field"} + +func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Field") + case "name": + out.Values[i] = ec.___Field_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "description": - out.Values[i] = ec._KubeScore_description(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "path": - out.Values[i] = ec._KubeScore_path(ctx, field, obj) + out.Values[i] = ec.___Field_description(ctx, field, obj) + case "args": + out.Values[i] = ec.___Field_args(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "summary": - out.Values[i] = ec._KubeScore_summary(ctx, field, obj) + case "type": + out.Values[i] = ec.___Field_type(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "fileName": - out.Values[i] = ec._KubeScore_fileName(ctx, field, obj) + case "isDeprecated": + out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "fileRow": - out.Values[i] = ec._KubeScore_fileRow(ctx, field, obj) + case "deprecationReason": + out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var __InputValueImplementors = []string{"__InputValue"} + +func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__InputValue") + case "name": + out.Values[i] = ec.___InputValue_name(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "eventTime": - out.Values[i] = ec._KubeScore_eventTime(ctx, field, obj) + case "description": + out.Values[i] = ec.___InputValue_description(ctx, field, obj) + case "type": + out.Values[i] = ec.___InputValue_type(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } + case "defaultValue": + out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -4048,34 +11999,35 @@ func (ec *executionContext) _KubeScore(ctx context.Context, sel ast.SelectionSet return out } -var namespaceDataImplementors = []string{"NamespaceData"} +var __SchemaImplementors = []string{"__Schema"} -func (ec *executionContext) _NamespaceData(ctx context.Context, sel ast.SelectionSet, obj *model.NamespaceData) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, namespaceDataImplementors) +func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("NamespaceData") - case "namespace": - out.Values[i] = ec._NamespaceData_namespace(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "outdatedImages": - out.Values[i] = ec._NamespaceData_outdatedImages(ctx, field, obj) + out.Values[i] = graphql.MarshalString("__Schema") + case "description": + out.Values[i] = ec.___Schema_description(ctx, field, obj) + case "types": + out.Values[i] = ec.___Schema_types(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "kubeScores": - out.Values[i] = ec._NamespaceData_kubeScores(ctx, field, obj) + case "queryType": + out.Values[i] = ec.___Schema_queryType(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "resources": - out.Values[i] = ec._NamespaceData_resources(ctx, field, obj) + case "mutationType": + out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) + case "subscriptionType": + out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) + case "directives": + out.Values[i] = ec.___Schema_directives(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -4102,57 +12054,40 @@ func (ec *executionContext) _NamespaceData(ctx context.Context, sel ast.Selectio return out } -var outdatedImageImplementors = []string{"OutdatedImage"} +var __TypeImplementors = []string{"__Type"} -func (ec *executionContext) _OutdatedImage(ctx context.Context, sel ast.SelectionSet, obj *model.OutdatedImage) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, outdatedImageImplementors) +func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("OutdatedImage") - case "clusterName": - out.Values[i] = ec._OutdatedImage_clusterName(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "namespace": - out.Values[i] = ec._OutdatedImage_namespace(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "pod": - out.Values[i] = ec._OutdatedImage_pod(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "currentImage": - out.Values[i] = ec._OutdatedImage_currentImage(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "currentTag": - out.Values[i] = ec._OutdatedImage_currentTag(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "latestVersion": - out.Values[i] = ec._OutdatedImage_latestVersion(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "versionsBehind": - out.Values[i] = ec._OutdatedImage_versionsBehind(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "eventTime": - out.Values[i] = ec._OutdatedImage_eventTime(ctx, field, obj) + out.Values[i] = graphql.MarshalString("__Type") + case "kind": + out.Values[i] = ec.___Type_kind(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } + case "name": + out.Values[i] = ec.___Type_name(ctx, field, obj) + case "description": + out.Values[i] = ec.___Type_description(ctx, field, obj) + case "fields": + out.Values[i] = ec.___Type_fields(ctx, field, obj) + case "interfaces": + out.Values[i] = ec.___Type_interfaces(ctx, field, obj) + case "possibleTypes": + out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) + case "enumValues": + out.Values[i] = ec.___Type_enumValues(ctx, field, obj) + case "inputFields": + out.Values[i] = ec.___Type_inputFields(ctx, field, obj) + case "ofType": + out.Values[i] = ec.___Type_ofType(ctx, field, obj) + case "specifiedByURL": + out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -4176,505 +12111,602 @@ func (ec *executionContext) _OutdatedImage(ctx context.Context, sel ast.Selectio return out } -var queryImplementors = []string{"Query"} +// endregion **************************** object.gotpl **************************** -func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, queryImplementors) - ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ - Object: "Query", - }) +// region ***************************** type.gotpl ***************************** - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ - Object: field.Name, - Field: field, - }) +func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + res, err := graphql.UnmarshalBoolean(v) + return res, graphql.ErrorOnPath(ctx, err) +} - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Query") - case "allNamespaceData": - field := field +func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + res := graphql.MarshalBoolean(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._Query_allNamespaceData(ctx, field) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) +func (ec *executionContext) marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.DeletedAPI) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNDeletedAPI2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPI(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNDeletedAPI2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPI(ctx context.Context, sel ast.SelectionSet, v *model.DeletedAPI) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._DeletedAPI(ctx, sel, v) +} + +func (ec *executionContext) marshalNDeprecatedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPIᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.DeprecatedAPI) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNDeprecatedAPI2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPI(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNDeprecatedAPI2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPI(ctx context.Context, sel ast.SelectionSet, v *model.DeprecatedAPI) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._DeprecatedAPI(ctx, sel, v) +} + +func (ec *executionContext) marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Event) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil } - return res + }() + if !isLen1 { + defer wg.Done() } + ret[i] = ec.marshalNEvent2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEvent(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } - rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, - func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - } + } + wg.Wait() - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "__type": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Query___type(ctx, field) - }) - case "__schema": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Query___schema(ctx, field) - }) - default: - panic("unknown field " + strconv.Quote(field.Name)) + for _, e := range ret { + if e == graphql.Null { + return graphql.Null } } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - atomic.AddInt32(&ec.deferred, int32(len(deferred))) + return ret +} - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) +func (ec *executionContext) marshalNEvent2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEvent(ctx context.Context, sel ast.SelectionSet, v *model.Event) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null } - - return out + return ec._Event(ctx, sel, v) } -var resourceImplementors = []string{"Resource"} - -func (ec *executionContext) _Resource(ctx context.Context, sel ast.SelectionSet, obj *model.Resource) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, resourceImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Resource") - case "clusterName": - out.Values[i] = ec._Resource_clusterName(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "namespace": - out.Values[i] = ec._Resource_namespace(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "kind": - out.Values[i] = ec._Resource_kind(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "resource": - out.Values[i] = ec._Resource_resource(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "age": - out.Values[i] = ec._Resource_age(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "eventTime": - out.Values[i] = ec._Resource_eventTime(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ +func (ec *executionContext) marshalNGetAllResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.GetAllResource) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() } - default: - panic("unknown field " + strconv.Quote(field.Name)) + ret[i] = ec.marshalNGetAllResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResource(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - atomic.AddInt32(&ec.deferred, int32(len(deferred))) + } + wg.Wait() - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } } - return out + return ret } -var __DirectiveImplementors = []string{"__Directive"} - -func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("__Directive") - case "name": - out.Values[i] = ec.___Directive_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "description": - out.Values[i] = ec.___Directive_description(ctx, field, obj) - case "locations": - out.Values[i] = ec.___Directive_locations(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "args": - out.Values[i] = ec.___Directive_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "isRepeatable": - out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) +func (ec *executionContext) marshalNGetAllResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResource(ctx context.Context, sel ast.SelectionSet, v *model.GetAllResource) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") } - } - out.Dispatch(ctx) - if out.Invalids > 0 { return graphql.Null } + return ec._GetAllResource(ctx, sel, v) +} - atomic.AddInt32(&ec.deferred, int32(len(deferred))) +func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalID(v) + return res, graphql.ErrorOnPath(ctx, err) +} - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) +func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalID(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } } - - return out + return res } -var __EnumValueImplementors = []string{"__EnumValue"} +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { + res, err := graphql.UnmarshalInt(v) + return res, graphql.ErrorOnPath(ctx, err) +} -func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + res := graphql.MarshalInt(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("__EnumValue") - case "name": - out.Values[i] = ec.___EnumValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "description": - out.Values[i] = ec.___EnumValue_description(ctx, field, obj) - case "isDeprecated": - out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ +func (ec *executionContext) marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.KubeScore) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() } - case "deprecationReason": - out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) + ret[i] = ec.marshalNKubeScore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScore(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - atomic.AddInt32(&ec.deferred, int32(len(deferred))) + } + wg.Wait() - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } } - return out + return ret } -var __FieldImplementors = []string{"__Field"} - -func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("__Field") - case "name": - out.Values[i] = ec.___Field_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "description": - out.Values[i] = ec.___Field_description(ctx, field, obj) - case "args": - out.Values[i] = ec.___Field_args(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "type": - out.Values[i] = ec.___Field_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "isDeprecated": - out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "deprecationReason": - out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) +func (ec *executionContext) marshalNKubeScore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScore(ctx context.Context, sel ast.SelectionSet, v *model.KubeScore) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") } - } - out.Dispatch(ctx) - if out.Invalids > 0 { return graphql.Null } + return ec._KubeScore(ctx, sel, v) +} - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) +func (ec *executionContext) marshalNKubescore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubescoreᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Kubescore) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNKubescore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubescore(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } - return out -} + } + wg.Wait() -var __InputValueImplementors = []string{"__InputValue"} + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } -func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + return ret +} - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("__InputValue") - case "name": - out.Values[i] = ec.___InputValue_name(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "description": - out.Values[i] = ec.___InputValue_description(ctx, field, obj) - case "type": - out.Values[i] = ec.___InputValue_type(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "defaultValue": - out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) +func (ec *executionContext) marshalNKubescore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubescore(ctx context.Context, sel ast.SelectionSet, v *model.Kubescore) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") } - } - out.Dispatch(ctx) - if out.Invalids > 0 { return graphql.Null } + return ec._Kubescore(ctx, sel, v) +} - atomic.AddInt32(&ec.deferred, int32(len(deferred))) +func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NamespaceData) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) } + wg.Wait() - return out -} + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } -var __SchemaImplementors = []string{"__Schema"} + return ret +} -func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) +func (ec *executionContext) marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx context.Context, sel ast.SelectionSet, v *model.NamespaceData) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._NamespaceData(ctx, sel, v) +} - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("__Schema") - case "description": - out.Values[i] = ec.___Schema_description(ctx, field, obj) - case "types": - out.Values[i] = ec.___Schema_types(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "queryType": - out.Values[i] = ec.___Schema_queryType(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "mutationType": - out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) - case "subscriptionType": - out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) - case "directives": - out.Values[i] = ec.___Schema_directives(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ +func (ec *executionContext) marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.OutdatedImage) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() } - default: - panic("unknown field " + strconv.Quote(field.Name)) + ret[i] = ec.marshalNOutdatedImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImage(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - atomic.AddInt32(&ec.deferred, int32(len(deferred))) + } + wg.Wait() - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } } - return out + return ret } -var __TypeImplementors = []string{"__Type"} - -func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("__Type") - case "kind": - out.Values[i] = ec.___Type_kind(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "name": - out.Values[i] = ec.___Type_name(ctx, field, obj) - case "description": - out.Values[i] = ec.___Type_description(ctx, field, obj) - case "fields": - out.Values[i] = ec.___Type_fields(ctx, field, obj) - case "interfaces": - out.Values[i] = ec.___Type_interfaces(ctx, field, obj) - case "possibleTypes": - out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) - case "enumValues": - out.Values[i] = ec.___Type_enumValues(ctx, field, obj) - case "inputFields": - out.Values[i] = ec.___Type_inputFields(ctx, field, obj) - case "ofType": - out.Values[i] = ec.___Type_ofType(ctx, field, obj) - case "specifiedByURL": - out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) - default: - panic("unknown field " + strconv.Quote(field.Name)) +func (ec *executionContext) marshalNOutdatedImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImage(ctx context.Context, sel ast.SelectionSet, v *model.OutdatedImage) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") } - } - out.Dispatch(ctx) - if out.Invalids > 0 { return graphql.Null } + return ec._OutdatedImage(ctx, sel, v) +} - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) +func (ec *executionContext) marshalNRakkess2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐRakkessᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Rakkess) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNRakkess2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐRakkess(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } - return out -} - -// endregion **************************** object.gotpl **************************** + } + wg.Wait() -// region ***************************** type.gotpl ***************************** + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } -func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { - res, err := graphql.UnmarshalBoolean(v) - return res, graphql.ErrorOnPath(ctx, err) + return ret } -func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { - res := graphql.MarshalBoolean(v) - if res == graphql.Null { +func (ec *executionContext) marshalNRakkess2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐRakkess(ctx context.Context, sel ast.SelectionSet, v *model.Rakkess) graphql.Marshaler { + if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } + return graphql.Null } - return res + return ec._Rakkess(ctx, sel, v) } -func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { - res, err := graphql.UnmarshalID(v) - return res, graphql.ErrorOnPath(ctx, err) +func (ec *executionContext) marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Resource) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResource(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret } -func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { - res := graphql.MarshalID(v) - if res == graphql.Null { +func (ec *executionContext) marshalNResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResource(ctx context.Context, sel ast.SelectionSet, v *model.Resource) graphql.Marshaler { + if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } + return graphql.Null } - return res + return ec._Resource(ctx, sel, v) } -func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { - res, err := graphql.UnmarshalInt(v) +func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { + res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { - res := graphql.MarshalInt(v) +func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") @@ -4683,7 +12715,7 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti return res } -func (ec *executionContext) marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.KubeScore) graphql.Marshaler { +func (ec *executionContext) marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivyImage) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -4707,7 +12739,7 @@ func (ec *executionContext) marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋku if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNKubeScore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScore(ctx, sel, v[i]) + ret[i] = ec.marshalNTrivyImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImage(ctx, sel, v[i]) } if isLen1 { f(i) @@ -4727,17 +12759,17 @@ func (ec *executionContext) marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋku return ret } -func (ec *executionContext) marshalNKubeScore2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScore(ctx context.Context, sel ast.SelectionSet, v *model.KubeScore) graphql.Marshaler { +func (ec *executionContext) marshalNTrivyImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImage(ctx context.Context, sel ast.SelectionSet, v *model.TrivyImage) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._KubeScore(ctx, sel, v) + return ec._TrivyImage(ctx, sel, v) } -func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NamespaceData) graphql.Marshaler { +func (ec *executionContext) marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivyMisconfig) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -4761,7 +12793,7 @@ func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelops if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx, sel, v[i]) + ret[i] = ec.marshalNTrivyMisconfig2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfig(ctx, sel, v[i]) } if isLen1 { f(i) @@ -4781,17 +12813,17 @@ func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelops return ret } -func (ec *executionContext) marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx context.Context, sel ast.SelectionSet, v *model.NamespaceData) graphql.Marshaler { +func (ec *executionContext) marshalNTrivyMisconfig2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfig(ctx context.Context, sel ast.SelectionSet, v *model.TrivyMisconfig) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._NamespaceData(ctx, sel, v) + return ec._TrivyMisconfig(ctx, sel, v) } -func (ec *executionContext) marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.OutdatedImage) graphql.Marshaler { +func (ec *executionContext) marshalNTrivySBOM2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbomᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivySbom) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -4815,7 +12847,7 @@ func (ec *executionContext) marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelops if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNOutdatedImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImage(ctx, sel, v[i]) + ret[i] = ec.marshalNTrivySBOM2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbom(ctx, sel, v[i]) } if isLen1 { f(i) @@ -4835,17 +12867,17 @@ func (ec *executionContext) marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelops return ret } -func (ec *executionContext) marshalNOutdatedImage2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImage(ctx context.Context, sel ast.SelectionSet, v *model.OutdatedImage) graphql.Marshaler { +func (ec *executionContext) marshalNTrivySBOM2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbom(ctx context.Context, sel ast.SelectionSet, v *model.TrivySbom) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._OutdatedImage(ctx, sel, v) + return ec._TrivySBOM(ctx, sel, v) } -func (ec *executionContext) marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Resource) graphql.Marshaler { +func (ec *executionContext) marshalNTrivyVul2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyVulᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivyVul) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -4869,7 +12901,7 @@ func (ec *executionContext) marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkub if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResource(ctx, sel, v[i]) + ret[i] = ec.marshalNTrivyVul2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyVul(ctx, sel, v[i]) } if isLen1 { f(i) @@ -4889,29 +12921,14 @@ func (ec *executionContext) marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkub return ret } -func (ec *executionContext) marshalNResource2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResource(ctx context.Context, sel ast.SelectionSet, v *model.Resource) graphql.Marshaler { +func (ec *executionContext) marshalNTrivyVul2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyVul(ctx context.Context, sel ast.SelectionSet, v *model.TrivyVul) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._Resource(ctx, sel, v) -} - -func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { - res, err := graphql.UnmarshalString(v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { - res := graphql.MarshalString(v) - if res == graphql.Null { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - } - return res + return ec._TrivyVul(ctx, sel, v) } func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { @@ -5193,6 +13210,22 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return res } +func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalInt(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOInt2ᚖint(ctx context.Context, sel ast.SelectionSet, v *int) graphql.Marshaler { + if v == nil { + return graphql.Null + } + res := graphql.MarshalInt(*v) + return res +} + func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { if v == nil { return nil, nil diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index 0e97567e..65a4b685 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -2,6 +2,57 @@ package model +type DeletedAPI struct { + ClusterName *string `json:"ClusterName,omitempty"` + ObjectName *string `json:"ObjectName,omitempty"` + Group *string `json:"Group,omitempty"` + Kind *string `json:"Kind,omitempty"` + Version *string `json:"Version,omitempty"` + Name *string `json:"Name,omitempty"` + Deleted *bool `json:"Deleted,omitempty"` + Scope *string `json:"Scope,omitempty"` + EventTime *string `json:"EventTime,omitempty"` + ExpiryDate *string `json:"ExpiryDate,omitempty"` +} + +type DeprecatedAPI struct { + ClusterName *string `json:"ClusterName,omitempty"` + ObjectName *string `json:"ObjectName,omitempty"` + Description *string `json:"Description,omitempty"` + Kind *string `json:"Kind,omitempty"` + Deprecated *bool `json:"Deprecated,omitempty"` + Scope *string `json:"Scope,omitempty"` + EventTime *string `json:"EventTime,omitempty"` + ExpiryDate *string `json:"ExpiryDate,omitempty"` +} + +type Event struct { + ClusterName *string `json:"ClusterName,omitempty"` + ID *string `json:"Id,omitempty"` + EventTime *string `json:"EventTime,omitempty"` + OpType *string `json:"OpType,omitempty"` + Name *string `json:"Name,omitempty"` + Namespace *string `json:"Namespace,omitempty"` + Kind *string `json:"Kind,omitempty"` + Message *string `json:"Message,omitempty"` + Reason *string `json:"Reason,omitempty"` + Host *string `json:"Host,omitempty"` + Event *string `json:"Event,omitempty"` + FirstTime *string `json:"FirstTime,omitempty"` + LastTime *string `json:"LastTime,omitempty"` + ExpiryDate *string `json:"ExpiryDate,omitempty"` +} + +type GetAllResource struct { + ClusterName *string `json:"ClusterName,omitempty"` + Namespace *string `json:"Namespace,omitempty"` + Kind *string `json:"Kind,omitempty"` + Resource *string `json:"Resource,omitempty"` + Age *string `json:"Age,omitempty"` + EventTime *string `json:"EventTime,omitempty"` + ExpiryDate *string `json:"ExpiryDate,omitempty"` +} + type KubeScore struct { ID string `json:"id"` ClusterName string `json:"clusterName"` @@ -19,6 +70,24 @@ type KubeScore struct { EventTime string `json:"eventTime"` } +type Kubescore struct { + ID string `json:"id"` + ClusterName *string `json:"clusterName,omitempty"` + ObjectName *string `json:"objectName,omitempty"` + Kind *string `json:"kind,omitempty"` + APIVersion *string `json:"apiVersion,omitempty"` + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` + TargetType *string `json:"targetType,omitempty"` + Description *string `json:"description,omitempty"` + Path *string `json:"path,omitempty"` + Summary *string `json:"summary,omitempty"` + FileName *string `json:"fileName,omitempty"` + FileRow *int `json:"fileRow,omitempty"` + EventTime *string `json:"eventTime,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` +} + type NamespaceData struct { Namespace string `json:"namespace"` OutdatedImages []*OutdatedImage `json:"outdatedImages"` @@ -40,6 +109,17 @@ type OutdatedImage struct { type Query struct { } +type Rakkess struct { + ClusterName *string `json:"ClusterName,omitempty"` + Name *string `json:"Name,omitempty"` + Create *string `json:"Create,omitempty"` + Delete *string `json:"Delete,omitempty"` + List *string `json:"List,omitempty"` + Update *string `json:"Update,omitempty"` + EventTime *string `json:"EventTime,omitempty"` + ExpiryDate *string `json:"ExpiryDate,omitempty"` +} + type Resource struct { ClusterName string `json:"clusterName"` Namespace string `json:"namespace"` @@ -48,3 +128,72 @@ type Resource struct { Age string `json:"age"` EventTime string `json:"eventTime"` } + +type TrivyImage struct { + ID string `json:"id"` + ClusterName *string `json:"clusterName,omitempty"` + ArtifactName *string `json:"artifactName,omitempty"` + VulID *string `json:"vulId,omitempty"` + VulPkgID *string `json:"vulPkgId,omitempty"` + VulPkgName *string `json:"vulPkgName,omitempty"` + VulInstalledVersion *string `json:"vulInstalledVersion,omitempty"` + VulFixedVersion *string `json:"vulFixedVersion,omitempty"` + VulTitle *string `json:"vulTitle,omitempty"` + VulSeverity *string `json:"vulSeverity,omitempty"` + VulPublishedDate *string `json:"vulPublishedDate,omitempty"` + VulLastModifiedDate *string `json:"vulLastModifiedDate,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` +} + +type TrivyMisconfig struct { + ID string `json:"id"` + ClusterName *string `json:"clusterName,omitempty"` + Namespace *string `json:"namespace,omitempty"` + Kind *string `json:"kind,omitempty"` + Name *string `json:"name,omitempty"` + MisconfigID *string `json:"misconfigId,omitempty"` + MisconfigAvdid *string `json:"misconfigAvdid,omitempty"` + MisconfigType *string `json:"misconfigType,omitempty"` + MisconfigTitle *string `json:"misconfigTitle,omitempty"` + MisconfigDesc *string `json:"misconfigDesc,omitempty"` + MisconfigMsg *string `json:"misconfigMsg,omitempty"` + MisconfigQuery *string `json:"misconfigQuery,omitempty"` + MisconfigResolution *string `json:"misconfigResolution,omitempty"` + MisconfigSeverity *string `json:"misconfigSeverity,omitempty"` + MisconfigStatus *string `json:"misconfigStatus,omitempty"` + EventTime *string `json:"eventTime,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` +} + +type TrivySbom struct { + ID string `json:"id"` + ClusterName *string `json:"clusterName,omitempty"` + ImageName *string `json:"imageName,omitempty"` + PackageName *string `json:"packageName,omitempty"` + PackageURL *string `json:"packageUrl,omitempty"` + BomRef *string `json:"bomRef,omitempty"` + SerialNumber *string `json:"serialNumber,omitempty"` + Version *int `json:"version,omitempty"` + BomFormat *string `json:"bomFormat,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` +} + +type TrivyVul struct { + ID string `json:"id"` + ClusterName *string `json:"clusterName,omitempty"` + Namespace *string `json:"namespace,omitempty"` + Kind *string `json:"kind,omitempty"` + Name *string `json:"name,omitempty"` + VulID *string `json:"vulId,omitempty"` + VulVendorIds *string `json:"vulVendorIds,omitempty"` + VulPkgID *string `json:"vulPkgId,omitempty"` + VulPkgName *string `json:"vulPkgName,omitempty"` + VulPkgPath *string `json:"vulPkgPath,omitempty"` + VulInstalledVersion *string `json:"vulInstalledVersion,omitempty"` + VulFixedVersion *string `json:"vulFixedVersion,omitempty"` + VulTitle *string `json:"vulTitle,omitempty"` + VulSeverity *string `json:"vulSeverity,omitempty"` + VulPublishedDate *string `json:"vulPublishedDate,omitempty"` + VulLastModifiedDate *string `json:"vulLastModifiedDate,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` +} diff --git a/graphqlserver/graph/resolver.go b/graphqlserver/graph/resolver.go index a940582c..37fad417 100644 --- a/graphqlserver/graph/resolver.go +++ b/graphqlserver/graph/resolver.go @@ -2,11 +2,6 @@ package graph import ( "database/sql" - "log" - - "github.com/intelops/kubviz/client/pkg/clickhouse" - "github.com/intelops/kubviz/client/pkg/config" - "github.com/kelseyhightower/envconfig" ) // This file will not be regenerated automatically. @@ -17,15 +12,6 @@ type Resolver struct { DB *sql.DB } -func NewResolver() *Resolver { - log.Println("Client Application started...") - cfg := &config.Config{} - if err := envconfig.Process("", cfg); err != nil { - log.Fatalf("Could not parse env Config: %v", err) - } - _, db, err := clickhouse.NewDBClient(cfg) - if err != nil { - log.Fatal(err) - } +func NewResolver(db *sql.DB) *Resolver { return &Resolver{DB: db} } diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index 2451272c..62d7908b 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -1,5 +1,136 @@ type Query { allNamespaceData: [NamespaceData!]! + allEvents: [Event!]! + allRakkess: [Rakkess!]! + allDeprecatedAPIs: [DeprecatedAPI!]! + allDeletedAPIs: [DeletedAPI!]! + allGetAllResources: [GetAllResource!]! + allTrivySBOMs: [TrivySBOM!]! + allTrivyImages: [TrivyImage!]! + allKubeScores: [Kubescore!]! + allTrivyVuls: [TrivyVul!]! + allTrivyMisconfigs: [TrivyMisconfig!]! +} + +type TrivyMisconfig { + id: ID! + clusterName: String + namespace: String + kind: String + name: String + misconfigId: String + misconfigAvdid: String + misconfigType: String + misconfigTitle: String + misconfigDesc: String + misconfigMsg: String + misconfigQuery: String + misconfigResolution: String + misconfigSeverity: String + misconfigStatus: String + eventTime: String + expiryDate: String +} + +type TrivyVul { + id: ID! + clusterName: String + namespace: String + kind: String + name: String + vulId: String + vulVendorIds: String + vulPkgId: String + vulPkgName: String + vulPkgPath: String + vulInstalledVersion: String + vulFixedVersion: String + vulTitle: String + vulSeverity: String + vulPublishedDate: String + vulLastModifiedDate: String + expiryDate: String +} + +type Kubescore { + id: ID! + clusterName: String + objectName: String + kind: String + apiVersion: String + name: String + namespace: String + targetType: String + description: String + path: String + summary: String + fileName: String + fileRow: Int + eventTime: String + expiryDate: String +} + +type TrivyImage { + id: ID! + clusterName: String + artifactName: String + vulId: String + vulPkgId: String + vulPkgName: String + vulInstalledVersion: String + vulFixedVersion: String + vulTitle: String + vulSeverity: String + vulPublishedDate: String + vulLastModifiedDate: String + expiryDate: String +} + +type TrivySBOM { + id: ID! + clusterName: String + imageName: String + packageName: String + packageUrl: String + bomRef: String + serialNumber: String + version: Int + bomFormat: String + expiryDate: String +} + +type GetAllResource { + ClusterName: String + Namespace: String + Kind: String + Resource: String + Age: String + EventTime: String + ExpiryDate: String +} + +type DeletedAPI { + ClusterName: String + ObjectName: String + Group: String + Kind: String + Version: String + Name: String + Deleted: Boolean + Scope: String + EventTime: String + ExpiryDate: String +} + +type DeprecatedAPI { + ClusterName: String + ObjectName: String + Description: String + Kind: String + Deprecated: Boolean + Scope: String + EventTime: String + ExpiryDate: String } type NamespaceData { @@ -45,3 +176,31 @@ type Resource { age: String! eventTime: String! } + +type Event { + ClusterName: String + Id: String + EventTime: String + OpType: String + Name: String + Namespace: String + Kind: String + Message: String + Reason: String + Host: String + Event: String + FirstTime: String + LastTime: String + ExpiryDate: String +} + +type Rakkess { + ClusterName: String + Name: String + Create: String + Delete: String + List: String + Update: String + EventTime: String + ExpiryDate: String +} diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index ff52def3..e70bc4df 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -6,36 +6,38 @@ package graph import ( "context" + "database/sql" "fmt" "github.com/intelops/kubviz/graphqlserver/graph/model" ) +// AllNamespaceData is the resolver for the allNamespaceData field. func (r *queryResolver) AllNamespaceData(ctx context.Context) ([]*model.NamespaceData, error) { - var namespaceDataList []*model.NamespaceData - resolver := NewResolver() - namespaces, err := r.fetchNamespacesFromDatabase(ctx) if err != nil { return nil, fmt.Errorf("error fetching namespaces: %v", err) } for _, ns := range namespaces { - outdatedImages, err := resolver.fetchOutdatedImages(ctx, ns) + outdatedImages, err := r.fetchOutdatedImages(ctx, ns) if err != nil { - return nil, fmt.Errorf("error fetching outdated images for namespace %s: %v", ns, err) + fmt.Printf("error fetching outdated images for namespace %s: %v\n", ns, err) + outdatedImages = []*model.OutdatedImage{} } - kubeScores, err := resolver.fetchKubeScores(ctx, ns) + kubeScores, err := r.fetchKubeScores(ctx, ns) if err != nil { - return nil, fmt.Errorf("error fetching kube scores for namespace %s: %v", ns, err) + fmt.Printf("error fetching kube scores for namespace %s: %v\n", ns, err) + kubeScores = []*model.KubeScore{} } - resources, err := resolver.fetchResources(ctx, ns) + resources, err := r.fetchResources(ctx, ns) if err != nil { - return nil, fmt.Errorf("error fetching resources for namespace %s: %v", ns, err) + fmt.Printf("error fetching resources for namespace %s: %v\n", ns, err) + resources = []*model.Resource{} } nd := &model.NamespaceData{ @@ -50,104 +52,172 @@ func (r *queryResolver) AllNamespaceData(ctx context.Context) ([]*model.Namespac return namespaceDataList, nil } -func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, error) { + +// AllEvents is the resolver for the allEvents field. +func (r *queryResolver) AllEvents(ctx context.Context) ([]*model.Event, error) { if r.DB == nil { return nil, fmt.Errorf("database connection is not initialized") } - query := `SELECT DISTINCT Namespace FROM events` + + query := `SELECT ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, FirstTime, LastTime, ExpiryDate FROM events` rows, err := r.DB.QueryContext(ctx, query) if err != nil { + if err == sql.ErrNoRows { + return []*model.Event{}, nil + } return nil, fmt.Errorf("error executing query: %v", err) } defer rows.Close() - var namespaces []string + var events []*model.Event for rows.Next() { - var namespace string - if err := rows.Scan(&namespace); err != nil { + var e model.Event + if err := rows.Scan(&e.ClusterName, &e.ID, &e.EventTime, &e.OpType, &e.Name, &e.Namespace, &e.Kind, &e.Message, &e.Reason, &e.Host, &e.Event, &e.FirstTime, &e.LastTime, &e.ExpiryDate); err != nil { return nil, fmt.Errorf("error scanning row: %v", err) } - namespaces = append(namespaces, namespace) + events = append(events, &e) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating rows: %v", err) } - return namespaces, nil + return events, nil } -func (r *Resolver) fetchOutdatedImages(ctx context.Context, namespace string) ([]*model.OutdatedImage, error) { + +// AllRakkess is the resolver for the allRakkess field. +func (r *queryResolver) AllRakkess(ctx context.Context) ([]*model.Rakkess, error) { if r.DB == nil { return nil, fmt.Errorf("database connection is not initialized") } - query := `SELECT ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime FROM outdated_images WHERE Namespace = ?` - rows, err := r.DB.QueryContext(ctx, query, namespace) + query := `SELECT ClusterName, Name, Create, Delete, List, Update, EventTime, ExpiryDate FROM rakkess` + + rows, err := r.DB.QueryContext(ctx, query) if err != nil { + if err == sql.ErrNoRows { + return []*model.Rakkess{}, nil + } return nil, fmt.Errorf("error executing query: %v", err) } defer rows.Close() - var outdatedImages []*model.OutdatedImage + var rakkessRecords []*model.Rakkess for rows.Next() { - var oi model.OutdatedImage - if err := rows.Scan(&oi.ClusterName, &oi.Namespace, &oi.Pod, &oi.CurrentImage, &oi.CurrentTag, &oi.LatestVersion, &oi.VersionsBehind, &oi.EventTime); err != nil { + var r model.Rakkess + if err := rows.Scan(&r.ClusterName, &r.Name, &r.Create, &r.Delete, &r.List, &r.Update, &r.EventTime, &r.ExpiryDate); err != nil { return nil, fmt.Errorf("error scanning row: %v", err) } - outdatedImages = append(outdatedImages, &oi) + rakkessRecords = append(rakkessRecords, &r) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating rows: %v", err) } - return outdatedImages, nil + return rakkessRecords, nil } -func (r *Resolver) fetchKubeScores(ctx context.Context, namespace string) ([]*model.KubeScore, error) { +// AllDeprecatedAPIs is the resolver for the allDeprecatedAPIs field. +func (r *queryResolver) AllDeprecatedAPIs(ctx context.Context) ([]*model.DeprecatedAPI, error) { if r.DB == nil { return nil, fmt.Errorf("database connection is not initialized") } - query := `SELECT id, ClusterName, ObjectName, Kind, ApiVersion, Name, Namespace, TargetType, Description, Path, Summary, FileName, FileRow, EventTime FROM kubescore WHERE Namespace = ?` - rows, err := r.DB.QueryContext(ctx, query, namespace) + query := `SELECT ClusterName, ObjectName, Description, Kind, Deprecated, Scope, EventTime, ExpiryDate FROM DeprecatedAPIs` + + rows, err := r.DB.QueryContext(ctx, query) if err != nil { + if err == sql.ErrNoRows { + return []*model.DeprecatedAPI{}, nil + } return nil, fmt.Errorf("error executing query: %v", err) } defer rows.Close() - var kubeScores []*model.KubeScore + var deprecatedAPIs []*model.DeprecatedAPI for rows.Next() { - var ks model.KubeScore - if err := rows.Scan(&ks.ID, &ks.ClusterName, &ks.ObjectName, &ks.Kind, &ks.APIVersion, &ks.Name, &ks.Namespace, &ks.TargetType, &ks.Description, &ks.Path, &ks.Summary, &ks.FileName, &ks.FileRow, &ks.EventTime); err != nil { + var d model.DeprecatedAPI + var deprecatedInt uint8 + if err := rows.Scan(&d.ClusterName, &d.ObjectName, &d.Description, &d.Kind, &deprecatedInt, &d.Scope, &d.EventTime, &d.ExpiryDate); err != nil { return nil, fmt.Errorf("error scanning row: %v", err) } - kubeScores = append(kubeScores, &ks) + + // Convert uint8 to bool + deprecatedBool := deprecatedInt != 0 + d.Deprecated = &deprecatedBool + + deprecatedAPIs = append(deprecatedAPIs, &d) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("error iterating rows: %v", err) } - return kubeScores, nil + return deprecatedAPIs, nil } -func (r *Resolver) fetchResources(ctx context.Context, namespace string) ([]*model.Resource, error) { + +// AllDeletedAPIs is the resolver for the allDeletedAPIs field. +func (r *queryResolver) AllDeletedAPIs(ctx context.Context) ([]*model.DeletedAPI, error) { if r.DB == nil { return nil, fmt.Errorf("database connection is not initialized") } - query := `SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime FROM getall_resources WHERE Namespace = ?` - rows, err := r.DB.QueryContext(ctx, query, namespace) + query := `SELECT ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope, EventTime, ExpiryDate FROM DeletedAPIs` + + rows, err := r.DB.QueryContext(ctx, query) if err != nil { + if err == sql.ErrNoRows { + return []*model.DeletedAPI{}, nil + } return nil, fmt.Errorf("error executing query: %v", err) } defer rows.Close() - var resources []*model.Resource + var deletedAPIs []*model.DeletedAPI for rows.Next() { - var res model.Resource - if err := rows.Scan(&res.ClusterName, &res.Namespace, &res.Kind, &res.Resource, &res.Age, &res.EventTime); err != nil { + var d model.DeletedAPI + var deletedInt uint8 + if err := rows.Scan(&d.ClusterName, &d.ObjectName, &d.Group, &d.Kind, &d.Version, &d.Name, &deletedInt, &d.Scope, &d.EventTime, &d.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + + // Convert uint8 to bool + deletedBool := deletedInt != 0 + d.Deleted = &deletedBool + + deletedAPIs = append(deletedAPIs, &d) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return deletedAPIs, nil +} + +// AllGetAllResources is the resolver for the allGetAllResources field. +func (r *queryResolver) AllGetAllResources(ctx context.Context) ([]*model.GetAllResource, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime, ExpiryDate FROM getall_resources` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + if err == sql.ErrNoRows { + return []*model.GetAllResource{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var resources []*model.GetAllResource + for rows.Next() { + var res model.GetAllResource + if err := rows.Scan(&res.ClusterName, &res.Namespace, &res.Kind, &res.Resource, &res.Age, &res.EventTime, &res.ExpiryDate); err != nil { return nil, fmt.Errorf("error scanning row: %v", err) } resources = append(resources, &res) @@ -160,6 +230,171 @@ func (r *Resolver) fetchResources(ctx context.Context, namespace string) ([]*mod return resources, nil } +// AllTrivySBOMs is the resolver for the allTrivySBOMs field. +func (r *queryResolver) AllTrivySBOMs(ctx context.Context) ([]*model.TrivySbom, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format, ExpiryDate FROM trivysbom` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + if err == sql.ErrNoRows { + return []*model.TrivySbom{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var sboms []*model.TrivySbom + for rows.Next() { + var s model.TrivySbom + if err := rows.Scan(&s.ID, &s.ClusterName, &s.ImageName, &s.PackageName, &s.PackageURL, &s.BomRef, &s.SerialNumber, &s.Version, &s.BomFormat, &s.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + sboms = append(sboms, &s) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return sboms, nil +} + +// AllTrivyImages is the resolver for the allTrivyImages field. +func (r *queryResolver) AllTrivyImages(ctx context.Context) ([]*model.TrivyImage, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate FROM trivyimage` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + if err == sql.ErrNoRows { + return []*model.TrivyImage{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var images []*model.TrivyImage + for rows.Next() { + var img model.TrivyImage + if err := rows.Scan(&img.ID, &img.ClusterName, &img.ArtifactName, &img.VulID, &img.VulPkgID, &img.VulPkgName, &img.VulInstalledVersion, &img.VulFixedVersion, &img.VulTitle, &img.VulSeverity, &img.VulPublishedDate, &img.VulLastModifiedDate, &img.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + images = append(images, &img) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return images, nil +} + +// AllKubeScores is the resolver for the allKubeScores field. +func (r *queryResolver) AllKubeScores(ctx context.Context) ([]*model.Kubescore, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT id, clustername, object_name, kind, apiVersion, name, namespace, target_type, description, path, summary, file_name, file_row, EventTime, ExpiryDate FROM kubescore` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + if err == sql.ErrNoRows { + return []*model.Kubescore{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var kubeScores []*model.Kubescore + for rows.Next() { + var ks model.Kubescore + if err := rows.Scan(&ks.ID, &ks.ClusterName, &ks.ObjectName, &ks.Kind, &ks.APIVersion, &ks.Name, &ks.Namespace, &ks.TargetType, &ks.Description, &ks.Path, &ks.Summary, &ks.FileName, &ks.FileRow, &ks.EventTime, &ks.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + kubeScores = append(kubeScores, &ks) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return kubeScores, nil +} + +// AllTrivyVuls is the resolver for the allTrivyVuls field. +func (r *queryResolver) AllTrivyVuls(ctx context.Context) ([]*model.TrivyVul, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate FROM trivy_vul` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + if err == sql.ErrNoRows { + return []*model.TrivyVul{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var trivyVuls []*model.TrivyVul + for rows.Next() { + var tv model.TrivyVul + if err := rows.Scan(&tv.ID, &tv.ClusterName, &tv.Namespace, &tv.Kind, &tv.Name, &tv.VulID, &tv.VulVendorIds, &tv.VulPkgID, &tv.VulPkgName, &tv.VulPkgPath, &tv.VulInstalledVersion, &tv.VulFixedVersion, &tv.VulTitle, &tv.VulSeverity, &tv.VulPublishedDate, &tv.VulLastModifiedDate, &tv.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + trivyVuls = append(trivyVuls, &tv) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return trivyVuls, nil +} + +// AllTrivyMisconfigs is the resolver for the allTrivyMisconfigs field. +func (r *queryResolver) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyMisconfig, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime, ExpiryDate FROM trivy_misconfig` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + if err == sql.ErrNoRows { + return []*model.TrivyMisconfig{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var misconfigs []*model.TrivyMisconfig + for rows.Next() { + var tm model.TrivyMisconfig + if err := rows.Scan(&tm.ID, &tm.ClusterName, &tm.Namespace, &tm.Kind, &tm.Name, &tm.MisconfigID, &tm.MisconfigAvdid, &tm.MisconfigType, &tm.MisconfigTitle, &tm.MisconfigDesc, &tm.MisconfigMsg, &tm.MisconfigQuery, &tm.MisconfigResolution, &tm.MisconfigSeverity, &tm.MisconfigStatus, &tm.EventTime, &tm.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + misconfigs = append(misconfigs, &tm) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return misconfigs, nil +} + // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } diff --git a/graphqlserver/graph/utils.go b/graphqlserver/graph/utils.go new file mode 100644 index 00000000..23959ee4 --- /dev/null +++ b/graphqlserver/graph/utils.go @@ -0,0 +1,129 @@ +package graph + +import ( + "context" + "database/sql" + "fmt" + + "github.com/intelops/kubviz/graphqlserver/graph/model" +) + +func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + query := `SELECT DISTINCT Namespace FROM events` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var namespaces []string + for rows.Next() { + var namespace string + if err := rows.Scan(&namespace); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + namespaces = append(namespaces, namespace) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return namespaces, nil +} +func (r *Resolver) fetchOutdatedImages(ctx context.Context, namespace string) ([]*model.OutdatedImage, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + query := `SELECT ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime FROM outdated_images WHERE Namespace = ?` + + rows, err := r.DB.QueryContext(ctx, query, namespace) + if err != nil { + if err == sql.ErrNoRows { + return []*model.OutdatedImage{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var outdatedImages []*model.OutdatedImage + for rows.Next() { + var oi model.OutdatedImage + if err := rows.Scan(&oi.ClusterName, &oi.Namespace, &oi.Pod, &oi.CurrentImage, &oi.CurrentTag, &oi.LatestVersion, &oi.VersionsBehind, &oi.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + outdatedImages = append(outdatedImages, &oi) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return outdatedImages, nil +} +func (r *Resolver) fetchKubeScores(ctx context.Context, namespace string) ([]*model.KubeScore, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT id, clustername, object_name, kind, apiVersion, name, namespace, target_type, description, path, summary, file_name, file_row, EventTime FROM kubescore WHERE namespace = ?` + rows, err := r.DB.QueryContext(ctx, query, namespace) + if err != nil { + if err == sql.ErrNoRows { + // No data for the namespace, return an empty slice + return []*model.KubeScore{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var kubeScores []*model.KubeScore + for rows.Next() { + var ks model.KubeScore + if err := rows.Scan(&ks.ID, &ks.ClusterName, &ks.ObjectName, &ks.Kind, &ks.APIVersion, &ks.Name, &ks.Namespace, &ks.TargetType, &ks.Description, &ks.Path, &ks.Summary, &ks.FileName, &ks.FileRow, &ks.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + kubeScores = append(kubeScores, &ks) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return kubeScores, nil +} +func (r *Resolver) fetchResources(ctx context.Context, namespace string) ([]*model.Resource, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := `SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime FROM getall_resources WHERE Namespace = ?` + rows, err := r.DB.QueryContext(ctx, query, namespace) + if err != nil { + if err == sql.ErrNoRows { + // No data for the namespace, return an empty slice + return []*model.Resource{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var resources []*model.Resource + for rows.Next() { + var res model.Resource + if err := rows.Scan(&res.ClusterName, &res.Namespace, &res.Kind, &res.Resource, &res.Age, &res.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + resources = append(resources, &res) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return resources, nil +} diff --git a/graphqlserver/server.go b/graphqlserver/server.go index 85282e33..f16730f5 100644 --- a/graphqlserver/server.go +++ b/graphqlserver/server.go @@ -1,24 +1,43 @@ package main import ( + "database/sql" "log" "net/http" "os" + "time" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" + "github.com/intelops/kubviz/client/pkg/clickhouse" + "github.com/intelops/kubviz/client/pkg/config" "github.com/intelops/kubviz/graphqlserver/graph" + "github.com/kelseyhightower/envconfig" ) const defaultPort = "8085" +const ( + maxRetries = 5 + retryDelay = 5 * time.Second +) func main() { + log.Println("Graph ql server starting ... Iteration one") + cfg := &config.Config{} + if err := envconfig.Process("", cfg); err != nil { + log.Fatalf("Could not parse env Config: %v", err) + } + db, err := initializeDatabase(cfg) + if err != nil { + log.Fatalf("Failed to initialize database: %v", err) + } + resolver := graph.NewResolver(db) port := os.Getenv("PORT") if port == "" { port = defaultPort } - srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) + srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: resolver})) http.Handle("/", playground.Handler("GraphQL playground", "/query")) http.Handle("/query", srv) @@ -26,3 +45,21 @@ func main() { log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) log.Fatal(http.ListenAndServe(":"+port, nil)) } + +func initializeDatabase(cfg *config.Config) (*sql.DB, error) { + var db *sql.DB + var err error + + for i := 0; i < maxRetries; i++ { + _, db, err = clickhouse.NewDBClient(cfg) + if err == nil { + log.Println("Successfully connected to the database") + return db, nil + } + log.Printf("Failed to connect to database, retrying (%d/%d): %v", i+1, maxRetries, err) + time.Sleep(retryDelay) + } + + // If the loop exits and the connection is not established, return the error + return nil, err +} From 4db4f46ea3d44fc7e37c4cc4259ffeafafe31b39 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 23 Jan 2024 22:19:30 +0530 Subject: [PATCH 185/263] Added external-secret yaml --- charts/agent/Chart.yaml | 2 +- charts/agent/templates/external_secret.yaml | 18 ++++++++++++++++++ charts/agent/values.yaml | 4 ++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 charts/agent/templates/external_secret.yaml diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 237225e5..57e0526b 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.11 +version: 1.1.12 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/external_secret.yaml b/charts/agent/templates/external_secret.yaml new file mode 100644 index 00000000..a856e725 --- /dev/null +++ b/charts/agent/templates/external_secret.yaml @@ -0,0 +1,18 @@ +{{- if .Values.externalSecrets.create }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: nats-external +spec: + refreshInterval: "10s" + secretStoreRef: + name: vault-store + kind: ClusterSecretStore + target: + name: nats-secret + data: + - secretKey: nats-token + remoteRef: + key: secret/generic/nats/auth-token + property: nats +{{- end }} \ No newline at end of file diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 916f6d57..f5b7b304 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -192,6 +192,10 @@ opentelemetry: appName: "kubviz" clusterName: "kubviz" + +externalSecrets: + create: false + nats: host: kubviz-client-nats auth: From 26ca9cb53c6c5ec95a24a8081e9d067febe0129e Mon Sep 17 00:00:00 2001 From: Akash LM Date: Wed, 24 Jan 2024 15:31:13 +0530 Subject: [PATCH 186/263] Added external-secret yaml im client chart --- charts/client/Chart.yaml | 2 +- charts/client/templates/external_secret.yaml | 18 ++++++++++++++++++ charts/client/values.yaml | 3 +++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 charts/client/templates/external_secret.yaml diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index eb21a4d7..5e2241fa 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.16 +version: 1.1.17 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/external_secret.yaml b/charts/client/templates/external_secret.yaml new file mode 100644 index 00000000..a856e725 --- /dev/null +++ b/charts/client/templates/external_secret.yaml @@ -0,0 +1,18 @@ +{{- if .Values.externalSecrets.create }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: nats-external +spec: + refreshInterval: "10s" + secretStoreRef: + name: vault-store + kind: ClusterSecretStore + target: + name: nats-secret + data: + - secretKey: nats-token + remoteRef: + key: secret/generic/nats/auth-token + property: nats +{{- end }} \ No newline at end of file diff --git a/charts/client/values.yaml b/charts/client/values.yaml index b73fe34b..094d0074 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -80,6 +80,9 @@ tolerations: [] affinity: {} +externalSecrets: + create: false + nats: enabled: true #Authentication setup From 7bf9ae3c73e529bf2a11f05ec45db23bec54fa9e Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 24 Jan 2024 15:46:38 +0530 Subject: [PATCH 187/263] nats-mtls --- agent/container/pkg/clients/nats_client.go | 16 +++- agent/git/pkg/clients/nats_client.go | 16 +++- agent/kubviz/k8smetrics_agent.go | 22 +++++- client/pkg/clients/clients.go | 16 +++- pkg/mtlsnats/mtlsnats.go | 86 ++++++++++++++++++++++ 5 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 pkg/mtlsnats/mtlsnats.go diff --git a/agent/container/pkg/clients/nats_client.go b/agent/container/pkg/clients/nats_client.go index 342fb2ea..c2975b23 100755 --- a/agent/container/pkg/clients/nats_client.go +++ b/agent/container/pkg/clients/nats_client.go @@ -7,6 +7,7 @@ import ( "time" "github.com/intelops/kubviz/agent/container/pkg/config" + "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/intelops/kubviz/pkg/opentelemetry" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -45,7 +46,20 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { fmt.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + //conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + log.Fatal("error while getting tls config ", err) + } + + conn, err := nats.Connect(conf.NatsAddress, + nats.Name("Github metrics"), + nats.Token(conf.NatsToken), + nats.Secure(tlsConfig), + ) + if err != nil { return nil, err } diff --git a/agent/git/pkg/clients/nats_client.go b/agent/git/pkg/clients/nats_client.go index b42f9dcf..129e91d5 100644 --- a/agent/git/pkg/clients/nats_client.go +++ b/agent/git/pkg/clients/nats_client.go @@ -6,6 +6,7 @@ import ( "github.com/intelops/kubviz/agent/git/pkg/config" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/intelops/kubviz/pkg/opentelemetry" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -34,7 +35,20 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { fmt.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + //conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + log.Fatal("error while getting tls config ", err) + } + conn, err := nats.Connect(conf.NatsAddress, + nats.Name("Github metrics"), + nats.Token(conf.NatsToken), + nats.Secure(tlsConfig), + ) + if err != nil { return nil, err } diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 2dd8f127..5c56c3a1 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -21,6 +21,7 @@ import ( "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/intelops/kubviz/pkg/opentelemetry" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -64,6 +65,11 @@ var ( // uncomment this line from Dockerfile.Kubviz (COPY --from=builder /workspace/civo /etc/myapp/civo) cluster_conf_loc string = os.Getenv("CONFIG_LOCATION") schedulingIntervalStr string = os.Getenv("SCHEDULING_INTERVAL") + + //mtls variables + CertificateFilePath string = os.Getenv("CERT_FILE") + KeyFilePath string = os.Getenv("KEY_FILE") + CAFilePath string = os.Getenv("CA_FILE") ) func main() { @@ -78,8 +84,22 @@ func main() { config *rest.Config clientset *kubernetes.Clientset ) + + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + log.Fatal("error while getting tls config ", err) + } + // connecting with nats ... - nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + //nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + nc, err := nats.Connect( + natsurl, + nats.Name("K8s Metrics"), + nats.Token(token), + nats.Secure(tlsConfig), + ) checkErr(err) // creating a jetstream connection using the nats connection js, err := nc.JetStream() diff --git a/client/pkg/clients/clients.go b/client/pkg/clients/clients.go index 0ac4fef2..07bad431 100644 --- a/client/pkg/clients/clients.go +++ b/client/pkg/clients/clients.go @@ -7,6 +7,7 @@ import ( "github.com/intelops/kubviz/client/pkg/clickhouse" "github.com/intelops/kubviz/client/pkg/config" + "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/nats-io/nats.go" ) @@ -21,7 +22,20 @@ func NewNATSContext(conf *config.Config, dbClient clickhouse.DBInterface) (*NATS log.Println("Waiting before connecting to NATS at:", conf.NatsAddress) time.Sleep(1 * time.Second) - conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + //conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + log.Fatal("error while getting tls config ", err) + } + conn, err := nats.Connect(conf.NatsAddress, + nats.Name("Github metrics"), + nats.Token(conf.NatsToken), + nats.Secure(tlsConfig), + ) + if err != nil { return nil, err } diff --git a/pkg/mtlsnats/mtlsnats.go b/pkg/mtlsnats/mtlsnats.go new file mode 100644 index 00000000..7985f2ff --- /dev/null +++ b/pkg/mtlsnats/mtlsnats.go @@ -0,0 +1,86 @@ +package mtlsnats + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io" + "os" +) + +var ( + CertificateFilePath string = os.Getenv("CERT_FILE") + KeyFilePath string = os.Getenv("KEY_FILE") + CAFilePath string = os.Getenv("CA_FILE") +) + +func ReadMtlsCerts(certificateFilePath, keyFilePath, CAFilePath string) (certPEM, keyPEM, CACertPEM []byte, err error) { + certPEM, err = ReadMtlsFileContents(certificateFilePath) + if err != nil { + err = fmt.Errorf("error while reading cert file: %w", err) + return + } + + keyPEM, err = ReadMtlsFileContents(keyFilePath) + if err != nil { + err = fmt.Errorf("error while reading key file: %w", err) + return + } + + CACertPEM, err = ReadMtlsFileContents(CAFilePath) + if err != nil { + err = fmt.Errorf("error while reading CAcert file: %w", err) + return + } + + return + + +} + +func OpenMtlsCertFile(filepath string) (f *os.File, err error) { + f, err = os.Open(filepath) + if err != nil { + return nil, fmt.Errorf("Failed to open mtls certificate file: %w",err) + } + return f, nil +} + +func ReadMtlsFileContents(filePath string) ([]byte, error) { + file,err := OpenMtlsCertFile(filePath) + if err != nil { + return nil,err + } + + defer file.Close() + + contents, err := io.ReadAll(file) + if err != nil { + return nil, fmt.Errorf("Error while reading file %s:%w",filePath, err) + } + + return contents,nil +} + +func GetTlsConfig() (*tls.Config, error) { + certPEM, keyPEM, CACertPEM, err := ReadMtlsCerts(CertificateFilePath, KeyFilePath, CAFilePath) + if err != nil { + return nil, fmt.Errorf("Unable to read mtls certificates %w", err) + + } + + cert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + return nil, fmt.Errorf("Error loading X509 key pair from PEM: %w", err) + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(CACertPEM) + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + InsecureSkipVerify: false, + } + + return tlsConfig, nil +} \ No newline at end of file From 065e3dd2a09fad5d4e59e66ab21bf3e5aa16e267 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 30 Jan 2024 17:13:32 +0530 Subject: [PATCH 188/263] review-changes --- agent/container/pkg/clients/nats_client.go | 42 ++++++++++------ agent/git/pkg/clients/nats_client.go | 43 ++++++++++------ agent/kubviz/k8smetrics_agent.go | 57 +++++++++++++--------- pkg/mtlsnats/mtlsnats.go | 41 ++++++++++------ 4 files changed, 114 insertions(+), 69 deletions(-) diff --git a/agent/container/pkg/clients/nats_client.go b/agent/container/pkg/clients/nats_client.go index c2975b23..d087bb6c 100755 --- a/agent/container/pkg/clients/nats_client.go +++ b/agent/container/pkg/clients/nats_client.go @@ -47,21 +47,33 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { time.Sleep(1 * time.Second) //conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) - tlsConfig, err := mtlsnats.GetTlsConfig() - if err != nil { - log.Println("error while getting tls config ", err) - time.Sleep(time.Minute * 30) - log.Fatal("error while getting tls config ", err) + var conn *nats.Conn + var err error + var mtlsConfig mtlsnats.MtlsConfig + + if mtlsConfig.IsEnabled { + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + } else { + conn, err = nats.Connect(conf.NatsAddress, + nats.Name("Github metrics"), + nats.Token(conf.NatsToken), + nats.Secure(tlsConfig), + ) + if err != nil { + log.Println("error while connecting with mtls ", err) + } + } + } - conn, err := nats.Connect(conf.NatsAddress, - nats.Name("Github metrics"), - nats.Token(conf.NatsToken), - nats.Secure(tlsConfig), - ) - - if err != nil { - return nil, err + if conn == nil { + conn, err = nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + if err != nil { + return nil, fmt.Errorf("error while connecting with token: %w", err) + } } ctx := &NATSContext{ @@ -131,12 +143,12 @@ func (n *NATSContext) Close() { // An error is returned if the publishing process fails, such as if the connection is lost or if there are issues with the JetStream. func (n *NATSContext) Publish(event []byte, repo string) error { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("container-nats-client") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "ContainerPublish") span.SetAttributes(attribute.String("repo-name", repo)) defer span.End() - + msg := nats.NewMsg(eventSubject) msg.Data = event msg.Header.Set("REPO_NAME", repo) diff --git a/agent/git/pkg/clients/nats_client.go b/agent/git/pkg/clients/nats_client.go index 129e91d5..7d6830b4 100644 --- a/agent/git/pkg/clients/nats_client.go +++ b/agent/git/pkg/clients/nats_client.go @@ -37,20 +37,33 @@ func NewNATSContext(conf *config.Config) (*NATSContext, error) { //conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) - tlsConfig, err := mtlsnats.GetTlsConfig() - if err != nil { - log.Println("error while getting tls config ", err) - time.Sleep(time.Minute * 30) - log.Fatal("error while getting tls config ", err) + var conn *nats.Conn + var err error + var mtlsConfig mtlsnats.MtlsConfig + + if mtlsConfig.IsEnabled { + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + } else { + conn, err = nats.Connect(conf.NatsAddress, + nats.Name("Github metrics"), + nats.Token(conf.NatsToken), + nats.Secure(tlsConfig), + ) + if err != nil { + log.Println("error while connecting with mtls ", err) + } + } + } - conn, err := nats.Connect(conf.NatsAddress, - nats.Name("Github metrics"), - nats.Token(conf.NatsToken), - nats.Secure(tlsConfig), - ) - if err != nil { - return nil, err + if conn == nil { + conn, err = nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + if err != nil { + return nil, fmt.Errorf("error while connecting with token: %w", err) + } } ctx := &NATSContext{ @@ -109,13 +122,13 @@ func (n *NATSContext) Close() { } func (n *NATSContext) Publish(metric []byte, repo string, eventkey model.EventKey, eventvalue model.EventValue) error { - - ctx:=context.Background() + + ctx := context.Background() tracer := otel.Tracer("git-nats-client") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "GitPublish") span.SetAttributes(attribute.String("repo-name", repo)) defer span.End() - + msg := nats.NewMsg(eventSubject) msg.Data = metric msg.Header.Set("GitProvider", repo) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 5c56c3a1..4fed67fa 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -65,11 +65,6 @@ var ( // uncomment this line from Dockerfile.Kubviz (COPY --from=builder /workspace/civo /etc/myapp/civo) cluster_conf_loc string = os.Getenv("CONFIG_LOCATION") schedulingIntervalStr string = os.Getenv("SCHEDULING_INTERVAL") - - //mtls variables - CertificateFilePath string = os.Getenv("CERT_FILE") - KeyFilePath string = os.Getenv("KEY_FILE") - CAFilePath string = os.Getenv("CA_FILE") ) func main() { @@ -85,22 +80,36 @@ func main() { clientset *kubernetes.Clientset ) - tlsConfig, err := mtlsnats.GetTlsConfig() - if err != nil { - log.Println("error while getting tls config ", err) - time.Sleep(time.Minute * 30) - log.Fatal("error while getting tls config ", err) + var mtlsConfig mtlsnats.MtlsConfig + var nc *nats.Conn + + if mtlsConfig.IsEnabled { + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + } else { + nc, err = nats.Connect( + natsurl, + nats.Name("K8s Metrics"), + nats.Token(token), + nats.Secure(tlsConfig), + ) + if err != nil { + log.Println("error while connecting with mtls ", err) + } + } + + } + + if nc == nil { + nc, err = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + checkErr(err) } // connecting with nats ... //nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - nc, err := nats.Connect( - natsurl, - nats.Name("K8s Metrics"), - nats.Token(token), - nats.Secure(tlsConfig), - ) - checkErr(err) + // creating a jetstream connection using the nats connection js, err := nc.JetStream() checkErr(err) @@ -131,7 +140,7 @@ func main() { log.Printf("Error shutting down tracer provider: %v", err) } }() - + go publishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { @@ -186,24 +195,24 @@ func main() { // with subject "METRICS.created" func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("kubviz-publish-metrics") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishMetrics") span.SetAttributes(attribute.String("kubviz-agent", "publish-metrics")) defer span.End() - + watchK8sEvents(clientset, js) errCh <- nil } func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext) (bool, error) { - - ctx:=context.Background() + + ctx := context.Background() tracer := otel.Tracer("kubviz-publish-k8smetrics") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishK8sMetrics") span.SetAttributes(attribute.String("kubviz-agent", "publish-k8smetrics")) defer span.End() - + metrics := model.Metrics{ ID: id, Type: mtype, @@ -299,7 +308,7 @@ func LogErr(err error) { } func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("kubviz-watch-k8sevents") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "watchK8sEvents") span.SetAttributes(attribute.String("kubviz-agent", "watch-k8sevents")) diff --git a/pkg/mtlsnats/mtlsnats.go b/pkg/mtlsnats/mtlsnats.go index 7985f2ff..26c22eb5 100644 --- a/pkg/mtlsnats/mtlsnats.go +++ b/pkg/mtlsnats/mtlsnats.go @@ -6,14 +6,17 @@ import ( "fmt" "io" "os" -) -var ( - CertificateFilePath string = os.Getenv("CERT_FILE") - KeyFilePath string = os.Getenv("KEY_FILE") - CAFilePath string = os.Getenv("CA_FILE") + "github.com/kelseyhightower/envconfig" ) +type MtlsConfig struct { + CertificateFilePath string `envconfig:"CERT_FILE" default:""` + KeyFilePath string `envconfig:"KEY_FILE" default:""` + CAFilePath string `envconfig:"CA_FILE" default:""` + IsEnabled bool `envconfig:"IS_ENABLED" default:"false"` +} + func ReadMtlsCerts(certificateFilePath, keyFilePath, CAFilePath string) (certPEM, keyPEM, CACertPEM []byte, err error) { certPEM, err = ReadMtlsFileContents(certificateFilePath) if err != nil { @@ -35,35 +38,43 @@ func ReadMtlsCerts(certificateFilePath, keyFilePath, CAFilePath string) (certPEM return - } func OpenMtlsCertFile(filepath string) (f *os.File, err error) { f, err = os.Open(filepath) if err != nil { - return nil, fmt.Errorf("Failed to open mtls certificate file: %w",err) + return nil, fmt.Errorf("Failed to open mtls certificate file: %w", err) } return f, nil } func ReadMtlsFileContents(filePath string) ([]byte, error) { - file,err := OpenMtlsCertFile(filePath) + file, err := OpenMtlsCertFile(filePath) if err != nil { - return nil,err + return nil, err } defer file.Close() contents, err := io.ReadAll(file) if err != nil { - return nil, fmt.Errorf("Error while reading file %s:%w",filePath, err) + return nil, fmt.Errorf("Error while reading file %s:%w", filePath, err) } - return contents,nil + return contents, nil } func GetTlsConfig() (*tls.Config, error) { - certPEM, keyPEM, CACertPEM, err := ReadMtlsCerts(CertificateFilePath, KeyFilePath, CAFilePath) + + var cfg MtlsConfig + err := envconfig.Process("", &cfg) + + if err != nil { + return nil, fmt.Errorf("Unable to read mtls config %w", err) + + } + + certPEM, keyPEM, CACertPEM, err := ReadMtlsCerts(cfg.CertificateFilePath, cfg.KeyFilePath, cfg.CAFilePath) if err != nil { return nil, fmt.Errorf("Unable to read mtls certificates %w", err) @@ -77,10 +88,10 @@ func GetTlsConfig() (*tls.Config, error) { caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(CACertPEM) tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, InsecureSkipVerify: false, } return tlsConfig, nil -} \ No newline at end of file +} From dea95c0cb1f060fe5acfc56fe56235f2de768779 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 30 Jan 2024 17:25:26 +0530 Subject: [PATCH 189/263] client-changes-mtls --- client/pkg/clients/clients.go | 36 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/client/pkg/clients/clients.go b/client/pkg/clients/clients.go index 07bad431..bee3fa2e 100644 --- a/client/pkg/clients/clients.go +++ b/client/pkg/clients/clients.go @@ -24,20 +24,32 @@ func NewNATSContext(conf *config.Config, dbClient clickhouse.DBInterface) (*NATS //conn, err := nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) - tlsConfig, err := mtlsnats.GetTlsConfig() - if err != nil { - log.Println("error while getting tls config ", err) - time.Sleep(time.Minute * 30) - log.Fatal("error while getting tls config ", err) + var conn *nats.Conn + var err error + var mtlsConfig mtlsnats.MtlsConfig + + if mtlsConfig.IsEnabled { + tlsConfig, err := mtlsnats.GetTlsConfig() + if err != nil { + log.Println("error while getting tls config ", err) + time.Sleep(time.Minute * 30) + } else { + conn, err = nats.Connect(conf.NatsAddress, + nats.Name("Github metrics"), + nats.Token(conf.NatsToken), + nats.Secure(tlsConfig), + ) + if err != nil { + log.Println("error while connecting with mtls ", err) + } + } } - conn, err := nats.Connect(conf.NatsAddress, - nats.Name("Github metrics"), - nats.Token(conf.NatsToken), - nats.Secure(tlsConfig), - ) - if err != nil { - return nil, err + if conn == nil { + conn, err = nats.Connect(conf.NatsAddress, nats.Name("Github metrics"), nats.Token(conf.NatsToken)) + if err != nil { + return nil, fmt.Errorf("error while connecting with token: %w", err) + } } ctx := &NATSContext{ From 44ee2c795cfe4a7c0dda661e119d413f42a5b6c2 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 30 Jan 2024 18:11:23 +0530 Subject: [PATCH 190/263] env-name-changed --- pkg/mtlsnats/mtlsnats.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mtlsnats/mtlsnats.go b/pkg/mtlsnats/mtlsnats.go index 26c22eb5..bc82cc94 100644 --- a/pkg/mtlsnats/mtlsnats.go +++ b/pkg/mtlsnats/mtlsnats.go @@ -14,7 +14,7 @@ type MtlsConfig struct { CertificateFilePath string `envconfig:"CERT_FILE" default:""` KeyFilePath string `envconfig:"KEY_FILE" default:""` CAFilePath string `envconfig:"CA_FILE" default:""` - IsEnabled bool `envconfig:"IS_ENABLED" default:"false"` + IsEnabled bool `envconfig:"ENABLE_MTLS_NATS" default:"false"` } func ReadMtlsCerts(certificateFilePath, keyFilePath, CAFilePath string) (certPEM, keyPEM, CACertPEM []byte, err error) { From 797e393e46615b085052387cd3782dbc1870cf56 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 6 Feb 2024 14:10:28 +0530 Subject: [PATCH 191/263] added aditional handlers --- graphqlserver/graph/generated.go | 871 ++++++++++++++++++++++-- graphqlserver/graph/model/models_gen.go | 6 + graphqlserver/graph/schema.graphqls | 11 + graphqlserver/graph/schema.resolvers.go | 129 ++++ 4 files changed, 969 insertions(+), 48 deletions(-) diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index 8d6f330b..ba3f13df 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -46,6 +46,12 @@ type DirectiveRoot struct { } type ComplexityRoot struct { + ClusterNamespaceOutdatedCount struct { + ClusterName func(childComplexity int) int + Namespace func(childComplexity int) int + OutdatedCount func(childComplexity int) int + } + DeletedAPI struct { ClusterName func(childComplexity int) int Deleted func(childComplexity int) int @@ -151,17 +157,22 @@ type ComplexityRoot struct { } Query struct { - AllDeletedAPIs func(childComplexity int) int - AllDeprecatedAPIs func(childComplexity int) int - AllEvents func(childComplexity int) int - AllGetAllResources func(childComplexity int) int - AllKubeScores func(childComplexity int) int - AllNamespaceData func(childComplexity int) int - AllRakkess func(childComplexity int) int - AllTrivyImages func(childComplexity int) int - AllTrivyMisconfigs func(childComplexity int) int - AllTrivySBOMs func(childComplexity int) int - AllTrivyVuls func(childComplexity int) int + AllClusterNamespaceOutdatedCounts func(childComplexity int) int + AllDeletedAPIs func(childComplexity int) int + AllDeprecatedAPIs func(childComplexity int) int + AllEvents func(childComplexity int) int + AllGetAllResources func(childComplexity int) int + AllKubeScores func(childComplexity int) int + AllNamespaceData func(childComplexity int) int + AllRakkess func(childComplexity int) int + AllTrivyImages func(childComplexity int) int + AllTrivyMisconfigs func(childComplexity int) int + AllTrivySBOMs func(childComplexity int) int + AllTrivyVuls func(childComplexity int) int + OutdatedImagesByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int + OutdatedImagesCount func(childComplexity int, clusterName string, namespace string) int + UniqueClusters func(childComplexity int) int + UniqueNamespaces func(childComplexity int) int } Rakkess struct { @@ -266,6 +277,11 @@ type QueryResolver interface { AllKubeScores(ctx context.Context) ([]*model.Kubescore, error) AllTrivyVuls(ctx context.Context) ([]*model.TrivyVul, error) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyMisconfig, error) + UniqueClusters(ctx context.Context) ([]string, error) + UniqueNamespaces(ctx context.Context) ([]string, error) + OutdatedImagesByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.OutdatedImage, error) + OutdatedImagesCount(ctx context.Context, clusterName string, namespace string) (int, error) + AllClusterNamespaceOutdatedCounts(ctx context.Context) ([]*model.ClusterNamespaceOutdatedCount, error) } type executableSchema struct { @@ -287,6 +303,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "ClusterNamespaceOutdatedCount.clusterName": + if e.complexity.ClusterNamespaceOutdatedCount.ClusterName == nil { + break + } + + return e.complexity.ClusterNamespaceOutdatedCount.ClusterName(childComplexity), true + + case "ClusterNamespaceOutdatedCount.namespace": + if e.complexity.ClusterNamespaceOutdatedCount.Namespace == nil { + break + } + + return e.complexity.ClusterNamespaceOutdatedCount.Namespace(childComplexity), true + + case "ClusterNamespaceOutdatedCount.outdatedCount": + if e.complexity.ClusterNamespaceOutdatedCount.OutdatedCount == nil { + break + } + + return e.complexity.ClusterNamespaceOutdatedCount.OutdatedCount(childComplexity), true + case "DeletedAPI.ClusterName": if e.complexity.DeletedAPI.ClusterName == nil { break @@ -847,6 +884,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.OutdatedImage.VersionsBehind(childComplexity), true + case "Query.allClusterNamespaceOutdatedCounts": + if e.complexity.Query.AllClusterNamespaceOutdatedCounts == nil { + break + } + + return e.complexity.Query.AllClusterNamespaceOutdatedCounts(childComplexity), true + case "Query.allDeletedAPIs": if e.complexity.Query.AllDeletedAPIs == nil { break @@ -924,6 +968,44 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.AllTrivyVuls(childComplexity), true + case "Query.outdatedImagesByClusterAndNamespace": + if e.complexity.Query.OutdatedImagesByClusterAndNamespace == nil { + break + } + + args, err := ec.field_Query_outdatedImagesByClusterAndNamespace_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.OutdatedImagesByClusterAndNamespace(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + + case "Query.outdatedImagesCount": + if e.complexity.Query.OutdatedImagesCount == nil { + break + } + + args, err := ec.field_Query_outdatedImagesCount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.OutdatedImagesCount(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + + case "Query.uniqueClusters": + if e.complexity.Query.UniqueClusters == nil { + break + } + + return e.complexity.Query.UniqueClusters(childComplexity), true + + case "Query.uniqueNamespaces": + if e.complexity.Query.UniqueNamespaces == nil { + break + } + + return e.complexity.Query.UniqueNamespaces(childComplexity), true + case "Rakkess.ClusterName": if e.complexity.Rakkess.ClusterName == nil { break @@ -1544,6 +1626,54 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Query_outdatedImagesByClusterAndNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 + return args, nil +} + +func (ec *executionContext) field_Query_outdatedImagesCount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 + return args, nil +} + func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1582,6 +1712,138 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** +func (ec *executionContext) _ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterNamespaceOutdatedCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ClusterNamespaceOutdatedCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterNamespaceOutdatedCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ClusterNamespaceOutdatedCount_outdatedCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OutdatedCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterNamespaceOutdatedCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DeletedAPI_ClusterName(ctx, field) if err != nil { @@ -5691,7 +5953,303 @@ func (ec *executionContext) _Query_allTrivyMisconfigs(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllTrivyMisconfigs(rctx) + return ec.resolvers.Query().AllTrivyMisconfigs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyMisconfig) + fc.Result = res + return ec.marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyMisconfig_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_TrivyMisconfig_namespace(ctx, field) + case "kind": + return ec.fieldContext_TrivyMisconfig_kind(ctx, field) + case "name": + return ec.fieldContext_TrivyMisconfig_name(ctx, field) + case "misconfigId": + return ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) + case "misconfigAvdid": + return ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) + case "misconfigType": + return ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) + case "misconfigTitle": + return ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) + case "misconfigDesc": + return ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) + case "misconfigMsg": + return ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) + case "misconfigQuery": + return ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) + case "misconfigResolution": + return ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) + case "misconfigSeverity": + return ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) + case "misconfigStatus": + return ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) + case "eventTime": + return ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyMisconfig", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_uniqueClusters(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().UniqueClusters(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_uniqueNamespaces(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().UniqueNamespaces(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_outdatedImagesByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_outdatedImagesByClusterAndNamespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().OutdatedImagesByClusterAndNamespace(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.OutdatedImage) + fc.Result = res + return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_outdatedImagesByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_OutdatedImage_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_OutdatedImage_namespace(ctx, field) + case "pod": + return ec.fieldContext_OutdatedImage_pod(ctx, field) + case "currentImage": + return ec.fieldContext_OutdatedImage_currentImage(ctx, field) + case "currentTag": + return ec.fieldContext_OutdatedImage_currentTag(ctx, field) + case "latestVersion": + return ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + case "versionsBehind": + return ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + case "eventTime": + return ec.fieldContext_OutdatedImage_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_outdatedImagesByClusterAndNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_outdatedImagesCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_outdatedImagesCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().OutdatedImagesCount(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_outdatedImagesCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_outdatedImagesCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterNamespaceOutdatedCounts(rctx) }) if err != nil { ec.Error(ctx, err) @@ -5703,12 +6261,12 @@ func (ec *executionContext) _Query_allTrivyMisconfigs(ctx context.Context, field } return graphql.Null } - res := resTmp.([]*model.TrivyMisconfig) + res := resTmp.([]*model.ClusterNamespaceOutdatedCount) fc.Result = res - return ec.marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx, field.Selections, res) + return ec.marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -5716,42 +6274,14 @@ func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Co IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "id": - return ec.fieldContext_TrivyMisconfig_id(ctx, field) case "clusterName": - return ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) + return ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) case "namespace": - return ec.fieldContext_TrivyMisconfig_namespace(ctx, field) - case "kind": - return ec.fieldContext_TrivyMisconfig_kind(ctx, field) - case "name": - return ec.fieldContext_TrivyMisconfig_name(ctx, field) - case "misconfigId": - return ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) - case "misconfigAvdid": - return ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) - case "misconfigType": - return ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) - case "misconfigTitle": - return ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) - case "misconfigDesc": - return ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) - case "misconfigMsg": - return ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) - case "misconfigQuery": - return ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) - case "misconfigResolution": - return ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) - case "misconfigSeverity": - return ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) - case "misconfigStatus": - return ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) - case "eventTime": - return ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) - case "expiryDate": - return ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) + return ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) + case "outdatedCount": + return ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type TrivyMisconfig", field.Name) + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceOutdatedCount", field.Name) }, } return fc, nil @@ -10608,6 +11138,55 @@ func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Conte // region **************************** object.gotpl **************************** +var clusterNamespaceOutdatedCountImplementors = []string{"ClusterNamespaceOutdatedCount"} + +func (ec *executionContext) _ClusterNamespaceOutdatedCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceOutdatedCount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterNamespaceOutdatedCountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterNamespaceOutdatedCount") + case "clusterName": + out.Values[i] = ec._ClusterNamespaceOutdatedCount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._ClusterNamespaceOutdatedCount_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "outdatedCount": + out.Values[i] = ec._ClusterNamespaceOutdatedCount_outdatedCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var deletedAPIImplementors = []string{"DeletedAPI"} func (ec *executionContext) _DeletedAPI(ctx context.Context, sel ast.SelectionSet, obj *model.DeletedAPI) graphql.Marshaler { @@ -11381,6 +11960,116 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "uniqueClusters": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_uniqueClusters(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "uniqueNamespaces": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_uniqueNamespaces(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "outdatedImagesByClusterAndNamespace": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_outdatedImagesByClusterAndNamespace(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "outdatedImagesCount": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_outdatedImagesCount(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterNamespaceOutdatedCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterNamespaceOutdatedCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -12130,6 +12819,60 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.ClusterNamespaceOutdatedCount) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNClusterNamespaceOutdatedCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCount(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNClusterNamespaceOutdatedCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterNamespaceOutdatedCount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterNamespaceOutdatedCount(ctx, sel, v) +} + func (ec *executionContext) marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.DeletedAPI) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -12715,6 +13458,38 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S return res } +func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNString2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNString2string(ctx, sel, v[i]) + } + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivyImage) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index 65a4b685..3f28f8bc 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -2,6 +2,12 @@ package model +type ClusterNamespaceOutdatedCount struct { + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + OutdatedCount int `json:"outdatedCount"` +} + type DeletedAPI struct { ClusterName *string `json:"ClusterName,omitempty"` ObjectName *string `json:"ObjectName,omitempty"` diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index 62d7908b..dc755361 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -10,6 +10,17 @@ type Query { allKubeScores: [Kubescore!]! allTrivyVuls: [TrivyVul!]! allTrivyMisconfigs: [TrivyMisconfig!]! + uniqueClusters: [String!]! + uniqueNamespaces: [String!]! + outdatedImagesByClusterAndNamespace(clusterName: String!, namespace: String!): [OutdatedImage!]! + outdatedImagesCount(clusterName: String!, namespace: String!): Int! + allClusterNamespaceOutdatedCounts: [ClusterNamespaceOutdatedCount!]! +} + +type ClusterNamespaceOutdatedCount { + clusterName: String! + namespace: String! + outdatedCount: Int! } type TrivyMisconfig { diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index e70bc4df..e58892de 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -395,6 +395,135 @@ func (r *queryResolver) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyM return misconfigs, nil } +// UniqueClusters is the resolver for the uniqueClusters field. +func (r *queryResolver) UniqueClusters(ctx context.Context) ([]string, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + query := `SELECT DISTINCT ClusterName FROM events` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var clusters []string + for rows.Next() { + var cluster string + if err := rows.Scan(&cluster); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + clusters = append(clusters, cluster) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return clusters, nil +} + +// UniqueNamespaces is the resolver for the uniqueNamespaces field. +func (r *queryResolver) UniqueNamespaces(ctx context.Context) ([]string, error) { + return r.fetchNamespacesFromDatabase(ctx) +} + +// OutdatedImagesByClusterAndNamespace is the resolver for the outdatedImagesByClusterAndNamespace field. +func (r *queryResolver) OutdatedImagesByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.OutdatedImage, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := `SELECT ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime FROM outdated_images WHERE ClusterName = ? AND Namespace = ?` + + rows, err := r.DB.QueryContext(ctx, query, clusterName, namespace) + if err != nil { + if err == sql.ErrNoRows { + return []*model.OutdatedImage{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var outdatedImages []*model.OutdatedImage + for rows.Next() { + var oi model.OutdatedImage + if err := rows.Scan(&oi.ClusterName, &oi.Namespace, &oi.Pod, &oi.CurrentImage, &oi.CurrentTag, &oi.LatestVersion, &oi.VersionsBehind, &oi.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + outdatedImages = append(outdatedImages, &oi) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return outdatedImages, nil +} + +// OutdatedImagesCount is the resolver for the outdatedImagesCount field. +func (r *queryResolver) OutdatedImagesCount(ctx context.Context, clusterName string, namespace string) (int, error) { + if r.DB == nil { + return 0, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return 0, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := `SELECT COUNT(*) FROM outdated_images WHERE ClusterName = ? AND Namespace = ?` + + var count int + err := r.DB.QueryRowContext(ctx, query, clusterName, namespace).Scan(&count) + if err != nil { + if err == sql.ErrNoRows { + return 0, nil + } + return 0, fmt.Errorf("error executing query: %v", err) + } + + return count, nil +} + +// AllClusterNamespaceOutdatedCounts is the resolver for the allClusterNamespaceOutdatedCounts field. +func (r *queryResolver) AllClusterNamespaceOutdatedCounts(ctx context.Context) ([]*model.ClusterNamespaceOutdatedCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := ` + SELECT ClusterName, Namespace, COUNT(*) as outdatedCount + FROM outdated_images + GROUP BY ClusterName, Namespace + ` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var results []*model.ClusterNamespaceOutdatedCount + for rows.Next() { + var result model.ClusterNamespaceOutdatedCount + if err := rows.Scan(&result.ClusterName, &result.Namespace, &result.OutdatedCount); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + results = append(results, &result) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return results, nil +} + // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } From 5de599bf132fccf5716cf543b83eb2a2de92565e Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 9 Feb 2024 10:34:39 +0530 Subject: [PATCH 192/263] added consumer as env --- client/pkg/clients/kubviz_client.go | 29 +++++++++++++++++------------ client/pkg/config/config.go | 10 ++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index d4854dfa..3f984f98 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -7,11 +7,13 @@ import ( "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/pkg/opentelemetry" + "github.com/kelseyhightower/envconfig" "github.com/nats-io/nats.go" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "github.com/intelops/kubviz/client/pkg/clickhouse" + "github.com/intelops/kubviz/client/pkg/config" "github.com/intelops/kubviz/model" ) @@ -23,12 +25,15 @@ type SubscriptionInfo struct { func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("kubviz-client") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "SubscribeAllKubvizNats") span.SetAttributes(attribute.String("kubviz-subscribe", "subscribe")) defer span.End() - + cfg := &config.Config{} + if err := envconfig.Process("", cfg); err != nil { + log.Fatalf("Could not parse env Config: %v", err) + } subscribe := func(sub SubscriptionInfo) { n.stream.Subscribe(sub.Subject, sub.Handler, nats.Durable(sub.Consumer), nats.ManualAck()) } @@ -36,7 +41,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { subscriptions := []SubscriptionInfo{ { Subject: constants.KetallSubject, - Consumer: constants.KetallConsumer, + Consumer: cfg.KetallConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.Resource @@ -51,7 +56,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.RakeesSubject, - Consumer: constants.RakeesConsumer, + Consumer: cfg.RakeesConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.RakeesMetrics @@ -66,7 +71,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.OutdatedSubject, - Consumer: constants.OutdatedConsumer, + Consumer: cfg.OutdatedConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.CheckResultfinal @@ -81,7 +86,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.DeprecatedSubject, - Consumer: constants.DeprecatedConsumer, + Consumer: cfg.DeprecatedConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.DeprecatedAPI @@ -96,7 +101,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.DeletedSubject, - Consumer: constants.DeletedConsumer, + Consumer: cfg.DeletedConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.DeletedAPI @@ -111,7 +116,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.TRIVY_IMAGE_SUBJECT, - Consumer: constants.Trivy_Image_Consumer, + Consumer: cfg.TrivyImageConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.TrivyImage @@ -126,7 +131,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.TRIVY_SBOM_SUBJECT, - Consumer: constants.Trivy_Sbom_Consumer, + Consumer: cfg.TrivySbomConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.SbomData @@ -142,7 +147,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.KubvizSubject, - Consumer: constants.KubvizConsumer, + Consumer: cfg.KubvizConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.Metrics @@ -157,7 +162,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.KUBESCORE_SUBJECT, - Consumer: constants.KubscoreConsumer, + Consumer: cfg.KubscoreConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.KubeScoreRecommendations @@ -172,7 +177,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { }, { Subject: constants.TRIVY_K8S_SUBJECT, - Consumer: constants.TrivyConsumer, + Consumer: cfg.TrivyConsumer, Handler: func(msg *nats.Msg) { msg.Ack() var metrics model.Trivy diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index 3ebb9cf1..5f6e252f 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -7,4 +7,14 @@ type Config struct { DBAddress string `envconfig:"DB_ADDRESS"` ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` + KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER"` + RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER"` + OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER"` + DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER"` + DeletedConsumer string `envconfig:"DELETED_API_CONSUMER"` + KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER"` + KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER"` + TrivyConsumer string `envconfig:"TRIVY_CONSUMER"` + TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER"` + TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER"` } From c09ced2ba0ad1724b7f57db2002e57adb0a48605 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 9 Feb 2024 12:10:17 +0530 Subject: [PATCH 193/263] added consumer as env --- client/pkg/config/config.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index 5f6e252f..7f030aa9 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -7,14 +7,14 @@ type Config struct { DBAddress string `envconfig:"DB_ADDRESS"` ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` - KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER"` - RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER"` - OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER"` - DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER"` - DeletedConsumer string `envconfig:"DELETED_API_CONSUMER"` - KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER"` - KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER"` - TrivyConsumer string `envconfig:"TRIVY_CONSUMER"` - TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER"` - TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER"` + KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER" required:"true"` + RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER" required:"true"` + OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER" required:"true"` + DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER" required:"true"` + DeletedConsumer string `envconfig:"DELETED_API_CONSUMER" required:"true"` + KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER" required:"true"` + KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER" required:"true"` + TrivyConsumer string `envconfig:"TRIVY_CONSUMER" required:"true"` + TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER" required:"true"` + TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER" required:"true"` } From f016cb0dc175e30fa1e285aae2cec6770474495c Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 9 Feb 2024 13:15:36 +0530 Subject: [PATCH 194/263] consumer helm change --- charts/client/Chart.yaml | 2 +- charts/client/templates/deployment.yaml | 20 ++++++++++++++++++++ charts/client/values.yaml | 11 +++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 5e2241fa..1d01f41e 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.17 +version: 1.1.18 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index c13e6f84..27daebc0 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -145,6 +145,26 @@ spec: value: {{ .Values.opentelemetry.url }} - name : APPLICATION_NAME value : {{ .Values.opentelemetry.appName }} + - name : KETALL_EVENTS_CONSUMER + value : {{ .Values.consumer.ketallconsumer }} + - name : RAKEES_METRICS_CONSUMER + value : {{ .Values.consumer.rakeesconsumer }} + - name : OUTDATED_EVENTS_CONSUMER + value : {{ .Values.consumer.outdatedconsumer }} + - name : DEPRECATED_API_CONSUMER + value : {{ .Values.consumer.deprecatedconsumer }} + - name : DELETED_API_CONSUMER + value : {{ .Values.consumer.deletedconsumer }} + - name : KUBVIZ_EVENTS_CONSUMER + value : {{ .Values.consumer.kubvizconsumer }} + - name : KUBSCORE_CONSUMER + value : {{ .Values.consumer.kubscoreconsumer }} + - name : TRIVY_CONSUMER + value : {{ .Values.consumer.trivyconsumer }} + - name : TRIVY_IMAGE_CONSUMER + value : {{ .Values.consumer.trivyimageconsumer }} + - name : TRIVY_SBOM_CONSUMER + value : {{ .Values.consumer.trivysbomconsumer }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 094d0074..3e5add4f 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -177,3 +177,14 @@ opentelemetry: url: "otelcollector.local" appName: "kubviz" +consumer: + ketallconsumer: "KETALL_EVENTS_CONSUMER" + rakeesconsumer: "RAKEES_METRICS_CONSUMER" + outdatedconsumer: "OUTDATED_EVENTS_CONSUMER" + deprecatedconsumer: "DEPRECATED_API_CONSUMER" + deletedconsumer: "DELETED_API_CONSUMER" + kubvizconsumer: "KUBVIZ_EVENTS_CONSUMER" + kubscoreconsumer: "KUBSCORE_CONSUMER" + trivyconsumer: "TRIVY_CONSUMER" + trivyimageconsumer: "TRIVY_IMAGE_CONSUMER" + trivysbomconsumer: "TRIVY_SBOM_CONSUMER" \ No newline at end of file From 01df9a05a37184d02b324da17cd0e7e3854dac12 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 9 Feb 2024 14:39:14 +0530 Subject: [PATCH 195/263] kuberhealthy sql --- sql/0000021_kuberhealthy.down.sql | 1 + sql/0000021_kuberhealthy.up.sql | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 sql/0000021_kuberhealthy.down.sql create mode 100644 sql/0000021_kuberhealthy.up.sql diff --git a/sql/0000021_kuberhealthy.down.sql b/sql/0000021_kuberhealthy.down.sql new file mode 100644 index 00000000..e69f5b26 --- /dev/null +++ b/sql/0000021_kuberhealthy.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS kuberhealthy; diff --git a/sql/0000021_kuberhealthy.up.sql b/sql/0000021_kuberhealthy.up.sql new file mode 100644 index 00000000..62ce6e51 --- /dev/null +++ b/sql/0000021_kuberhealthy.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS kuberhealthy ( + CurrentUUID String, + CheckName String, + OK UInt8, + Errors String, + RunDuration String, + Namespace String, + Node String, + LastRun DateTime('UTC'), + AuthoritativePod String, + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} +) ENGINE = MergeTree() +ORDER BY ExpiryDate +TTL ExpiryDate; From 1caeaf0a0ed6bee919309e8ebd3597fc5f6026e9 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 9 Feb 2024 15:39:36 +0530 Subject: [PATCH 196/263] modified the code for readabilty --- agent/kubviz/application/application.go | 3 + agent/kubviz/k8smetrics_agent.go | 305 ++---------------- .../plugins/events/event_metrics_utils.go | 184 +++++++++++ agent/kubviz/{ => plugins/ketall}/ketall.go | 9 +- .../kubepreupgrade}/kubePreUpgrade.go | 6 +- .../{ => plugins/kubescore}/kube_score.go | 17 +- .../kubviz/{ => plugins/outdated}/outdated.go | 10 +- .../{ => plugins/rakkess}/rakees_agent.go | 33 +- agent/kubviz/{ => plugins}/rakkess/rakkess.go | 0 agent/kubviz/{ => plugins/trivy}/trivy.go | 12 +- .../kubviz/{ => plugins/trivy}/trivy_image.go | 14 +- .../kubviz/{ => plugins/trivy}/trivy_sbom.go | 63 ++-- agent/kubviz/scheduler.go | 88 ----- agent/kubviz/scheduler/scheduler.go | 158 +++++++++ .../kubviz/{ => scheduler}/scheduler_watch.go | 42 ++- 15 files changed, 494 insertions(+), 450 deletions(-) create mode 100644 agent/kubviz/application/application.go create mode 100644 agent/kubviz/plugins/events/event_metrics_utils.go rename agent/kubviz/{ => plugins/ketall}/ketall.go (96%) rename agent/kubviz/{ => plugins/kubepreupgrade}/kubePreUpgrade.go (99%) rename agent/kubviz/{ => plugins/kubescore}/kube_score.go (92%) rename agent/kubviz/{ => plugins/outdated}/outdated.go (99%) rename agent/kubviz/{ => plugins/rakkess}/rakees_agent.go (77%) rename agent/kubviz/{ => plugins}/rakkess/rakkess.go (100%) rename agent/kubviz/{ => plugins/trivy}/trivy.go (94%) rename agent/kubviz/{ => plugins/trivy}/trivy_image.go (87%) rename agent/kubviz/{ => plugins/trivy}/trivy_sbom.go (68%) delete mode 100644 agent/kubviz/scheduler.go create mode 100644 agent/kubviz/scheduler/scheduler.go rename agent/kubviz/{ => scheduler}/scheduler_watch.go (75%) diff --git a/agent/kubviz/application/application.go b/agent/kubviz/application/application.go new file mode 100644 index 00000000..301bc161 --- /dev/null +++ b/agent/kubviz/application/application.go @@ -0,0 +1,3 @@ +package application + +func Start() {} diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 4fed67fa..b99ddd91 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -1,45 +1,40 @@ package main import ( - "encoding/json" "log" "os" "os/signal" - "strconv" - "strings" "syscall" "time" - "github.com/intelops/go-common/logging" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "github.com/go-co-op/gocron" "github.com/nats-io/nats.go" "context" - "github.com/intelops/kubviz/constants" - "github.com/intelops/kubviz/model" "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/intelops/kubviz/pkg/opentelemetry" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - "fmt" - "github.com/intelops/kubviz/agent/config" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/fields" + "github.com/intelops/kubviz/agent/kubviz/plugins/events" + "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + + "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" + "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" + "github.com/intelops/kubviz/agent/kubviz/scheduler" + _ "k8s.io/client-go/plugin/pkg/client/auth/azure" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" "github.com/intelops/kubviz/agent/server" - "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" ) @@ -61,8 +56,6 @@ var ( natsurl string = os.Getenv("NATS_ADDRESS") //for local testing provide the location of kubeconfig - // inside the civo file paste your kubeconfig - // uncomment this line from Dockerfile.Kubviz (COPY --from=builder /workspace/civo /etc/myapp/civo) cluster_conf_loc string = os.Getenv("CONFIG_LOCATION") schedulingIntervalStr string = os.Getenv("SCHEDULING_INTERVAL") ) @@ -104,31 +97,24 @@ func main() { if nc == nil { nc, err = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - checkErr(err) + events.CheckErr(err) } - - // connecting with nats ... - //nc, err := nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - - // creating a jetstream connection using the nats connection js, err := nc.JetStream() - checkErr(err) - // creating a stream with stream name METRICS - err = createStream(js) - checkErr(err) - //setupAgent() + events.CheckErr(err) + err = events.CreateStream(js) + events.CheckErr(err) if env != Production { config, err = clientcmd.BuildConfigFromFlags("", cluster_conf_loc) if err != nil { log.Fatal(err) } - clientset = getK8sClient(config) + clientset = events.GetK8sClient(config) } else { config, err = rest.InClusterConfig() if err != nil { log.Fatal(err) } - clientset = getK8sClient(config) + clientset = events.GetK8sClient(config) } tp, err := opentelemetry.InitTracer() @@ -141,32 +127,31 @@ func main() { } }() - go publishMetrics(clientset, js, clusterMetricsChan) + go events.PublishMetrics(clientset, js, clusterMetricsChan) go server.StartServer() collectAndPublishMetrics := func() { - err := outDatedImages(config, js) - LogErr(err) - err = KubePreUpgradeDetector(config, js) - LogErr(err) - err = GetAllResources(config, js) - LogErr(err) - err = RakeesOutput(config, js) - LogErr(err) - // //getK8sEvents(clientset) - err = RunTrivySbomScan(config, js) - LogErr(err) - err = RunTrivyImageScans(config, js) - LogErr(err) - err = RunTrivyK8sClusterScan(js) - LogErr(err) - err = RunKubeScore(clientset, js) - LogErr(err) + err := outdated.OutDatedImages(config, js) + events.LogErr(err) + err = kubepreupgrade.KubePreUpgradeDetector(config, js) + events.LogErr(err) + err = ketall.GetAllResources(config, js) + events.LogErr(err) + err = rakkess.RakeesOutput(config, js) + events.LogErr(err) + err = trivy.RunTrivySbomScan(config, js) + events.LogErr(err) + err = trivy.RunTrivyImageScans(config, js) + events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(js) + events.LogErr(err) + err = kubescore.RunKubeScore(clientset, js) + events.LogErr(err) } collectAndPublishMetrics() if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. - scheduler := initScheduler(config, js, *cfg, clientset) + scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) // Start the scheduler scheduler.Start() @@ -190,225 +175,3 @@ func main() { s.StartBlocking() } } - -// publishMetrics publishes stream of events -// with subject "METRICS.created" -func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { - - ctx := context.Background() - tracer := otel.Tracer("kubviz-publish-metrics") - _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishMetrics") - span.SetAttributes(attribute.String("kubviz-agent", "publish-metrics")) - defer span.End() - - watchK8sEvents(clientset, js) - errCh <- nil -} - -func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext) (bool, error) { - - ctx := context.Background() - tracer := otel.Tracer("kubviz-publish-k8smetrics") - _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishK8sMetrics") - span.SetAttributes(attribute.String("kubviz-agent", "publish-k8smetrics")) - defer span.End() - - metrics := model.Metrics{ - ID: id, - Type: mtype, - Event: mdata, - ClusterName: ClusterName, - } - metricsJson, _ := json.Marshal(metrics) - _, err := js.Publish(constants.EventSubject, metricsJson) - if err != nil { - return true, err - } - log.Printf("Metrics with ID:%s has been published\n", id) - return false, nil -} - -// createStream creates a stream by using JetStreamContext -func createStream(js nats.JetStreamContext) error { - // Check if the METRICS stream already exists; if not, create it. - stream, err := js.StreamInfo(constants.StreamName) - log.Printf("Retrieved stream %s", fmt.Sprintf("%v", stream)) - if err != nil { - log.Printf("Error getting stream %s", err) - } - if stream == nil { - log.Printf("creating stream %q and subjects %q", constants.StreamName, constants.StreamSubjects) - _, err = js.AddStream(&nats.StreamConfig{ - Name: constants.StreamName, - Subjects: []string{constants.StreamSubjects}, - }) - checkErr(err) - } - return nil - -} - -func getK8sClient(config *rest.Config) *kubernetes.Clientset { - // create the clientset - clientset, err := kubernetes.NewForConfig(config) - checkErr(err) - return clientset -} - -func getK8sPods(clientset *kubernetes.Clientset) string { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - pods, err := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{}) - checkErr(err) - var sb strings.Builder - for i, pod := range pods.Items { - sb.WriteString("Name-" + strconv.Itoa(i) + ": ") - sb.WriteString(pod.Name) - sb.WriteString(" ") - sb.WriteString("Namespace-" + strconv.Itoa(i) + ": ") - sb.WriteString(pod.Namespace) - sb.WriteString(" ") - } - return sb.String() -} - -func getK8sNodes(clientset *kubernetes.Clientset) string { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) - checkErr(err) - var sb strings.Builder - for i, node := range nodes.Items { - sb.WriteString("Name-" + strconv.Itoa(i) + ": ") - sb.WriteString(node.Name) - } - return sb.String() -} - -func getK8sEvents(clientset *kubernetes.Clientset) string { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - events, err := clientset.CoreV1().Events("").List(ctx, metav1.ListOptions{}) - checkErr(err) - j, err := json.MarshalIndent(events, "", " ") - checkErr(err) - log.Printf(string(j)) - return string(j) -} - -func checkErr(err error) { - if err != nil { - log.Fatal(err) - } -} -func LogErr(err error) { - if err != nil { - log.Println(err) - } -} -func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { - - ctx := context.Background() - tracer := otel.Tracer("kubviz-watch-k8sevents") - _, span := tracer.Start(opentelemetry.BuildContext(ctx), "watchK8sEvents") - span.SetAttributes(attribute.String("kubviz-agent", "watch-k8sevents")) - defer span.End() - - watchlist := cache.NewListWatchFromClient( - clientset.CoreV1().RESTClient(), - "events", - v1.NamespaceAll, - fields.Everything(), - ) - _, controller := cache.NewInformer( - watchlist, - &v1.Event{}, - 0, // Duration is int64 - cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - event := obj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js) - }, - DeleteFunc: func(obj interface{}) { - event := obj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js) - }, - UpdateFunc: func(oldObj, newObj interface{}) { - event := newObj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js) - }, - }, - ) - stop := make(chan struct{}) - defer close(stop) - go controller.Run(stop) - - for { - time.Sleep(time.Second) - } -} -func initScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.AgentConfigurations, clientset *kubernetes.Clientset) (s *Scheduler) { - log := logging.NewLogger() - s = NewScheduler(log) - if cfg.OutdatedInterval != "" && cfg.OutdatedInterval != "0" { - sj, err := NewOutDatedImagesJob(config, js, cfg.OutdatedInterval) - if err != nil { - log.Fatal("no time interval", err) - } - err = s.AddJob("Outdated", sj) - if err != nil { - log.Fatal("failed to do job", err) - } - } - if cfg.GetAllInterval != "" && cfg.GetAllInterval != "0" { - sj, err := NewKetallJob(config, js, cfg.GetAllInterval) - if err != nil { - log.Fatal("no time interval", err) - } - err = s.AddJob("GetALL", sj) - if err != nil { - log.Fatal("failed to do job", err) - } - } - if cfg.KubeScoreInterval != "" && cfg.KubeScoreInterval != "0" { - sj, err := NewKubescoreJob(clientset, js, cfg.KubeScoreInterval) - if err != nil { - log.Fatal("no time interval", err) - } - err = s.AddJob("KubeScore", sj) - if err != nil { - log.Fatal("failed to do job", err) - } - } - if cfg.RakkessInterval != "" && cfg.RakkessInterval != "0" { - sj, err := NewRakkessJob(config, js, cfg.RakkessInterval) - if err != nil { - log.Fatal("no time interval", err) - } - err = s.AddJob("Rakkess", sj) - if err != nil { - log.Fatal("failed to do job", err) - } - } - if cfg.KubePreUpgradeInterval != "" && cfg.KubePreUpgradeInterval != "0" { - sj, err := NewKubePreUpgradeJob(config, js, cfg.KubePreUpgradeInterval) - if err != nil { - log.Fatal("no time interval", err) - } - err = s.AddJob("KubePreUpgrade", sj) - if err != nil { - log.Fatal("failed to do job", err) - } - } - if cfg.TrivyInterval != "" && cfg.TrivyInterval != "0" { - sj, err := NewTrivyJob(config, js, cfg.TrivyInterval) - if err != nil { - log.Fatal("no time interval", err) - } - err = s.AddJob("Trivy", sj) - if err != nil { - log.Fatal("failed to do job", err) - } - } - return -} diff --git a/agent/kubviz/plugins/events/event_metrics_utils.go b/agent/kubviz/plugins/events/event_metrics_utils.go new file mode 100644 index 00000000..15b2a8f2 --- /dev/null +++ b/agent/kubviz/plugins/events/event_metrics_utils.go @@ -0,0 +1,184 @@ +package events + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + "strconv" + "strings" + "time" + + "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" + "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" +) + +var ClusterName string = os.Getenv("CLUSTER_NAME") + +// publishMetrics publishes stream of events +// with subject "METRICS.created" +func PublishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, errCh chan error) { + + ctx := context.Background() + tracer := otel.Tracer("kubviz-publish-metrics") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishMetrics") + span.SetAttributes(attribute.String("kubviz-agent", "publish-metrics")) + defer span.End() + + watchK8sEvents(clientset, js) + errCh <- nil +} + +func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext) (bool, error) { + + ctx := context.Background() + tracer := otel.Tracer("kubviz-publish-k8smetrics") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishK8sMetrics") + span.SetAttributes(attribute.String("kubviz-agent", "publish-k8smetrics")) + defer span.End() + + metrics := model.Metrics{ + ID: id, + Type: mtype, + Event: mdata, + ClusterName: ClusterName, + } + metricsJson, _ := json.Marshal(metrics) + _, err := js.Publish(constants.EventSubject, metricsJson) + if err != nil { + return true, err + } + log.Printf("Metrics with ID:%s has been published\n", id) + return false, nil +} + +// createStream creates a stream by using JetStreamContext +func CreateStream(js nats.JetStreamContext) error { + // Check if the METRICS stream already exists; if not, create it. + stream, err := js.StreamInfo(constants.StreamName) + log.Printf("Retrieved stream %s", fmt.Sprintf("%v", stream)) + if err != nil { + log.Printf("Error getting stream %s", err) + } + if stream == nil { + log.Printf("creating stream %q and subjects %q", constants.StreamName, constants.StreamSubjects) + _, err = js.AddStream(&nats.StreamConfig{ + Name: constants.StreamName, + Subjects: []string{constants.StreamSubjects}, + }) + CheckErr(err) + } + return nil + +} + +func GetK8sClient(config *rest.Config) *kubernetes.Clientset { + // create the clientset + clientset, err := kubernetes.NewForConfig(config) + CheckErr(err) + return clientset +} + +func GetK8sPods(clientset *kubernetes.Clientset) string { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + pods, err := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{}) + CheckErr(err) + var sb strings.Builder + for i, pod := range pods.Items { + sb.WriteString("Name-" + strconv.Itoa(i) + ": ") + sb.WriteString(pod.Name) + sb.WriteString(" ") + sb.WriteString("Namespace-" + strconv.Itoa(i) + ": ") + sb.WriteString(pod.Namespace) + sb.WriteString(" ") + } + return sb.String() +} + +func GetK8sNodes(clientset *kubernetes.Clientset) string { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + CheckErr(err) + var sb strings.Builder + for i, node := range nodes.Items { + sb.WriteString("Name-" + strconv.Itoa(i) + ": ") + sb.WriteString(node.Name) + } + return sb.String() +} + +func GetK8sEvents(clientset *kubernetes.Clientset) string { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + events, err := clientset.CoreV1().Events("").List(ctx, metav1.ListOptions{}) + CheckErr(err) + j, err := json.MarshalIndent(events, "", " ") + CheckErr(err) + log.Printf("%#v", string(j)) + return string(j) +} + +func CheckErr(err error) { + if err != nil { + log.Fatal(err) + } +} +func LogErr(err error) { + if err != nil { + log.Println(err) + } +} +func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { + + ctx := context.Background() + tracer := otel.Tracer("kubviz-watch-k8sevents") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "watchK8sEvents") + span.SetAttributes(attribute.String("kubviz-agent", "watch-k8sevents")) + defer span.End() + + watchlist := cache.NewListWatchFromClient( + clientset.CoreV1().RESTClient(), + "events", + v1.NamespaceAll, + fields.Everything(), + ) + _, controller := cache.NewInformer( + watchlist, + &v1.Event{}, + 0, // Duration is int64 + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + event := obj.(*v1.Event) + publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js) + }, + DeleteFunc: func(obj interface{}) { + event := obj.(*v1.Event) + publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + event := newObj.(*v1.Event) + publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js) + }, + }, + ) + stop := make(chan struct{}) + defer close(stop) + go controller.Run(stop) + + for { + time.Sleep(time.Second) + } +} diff --git a/agent/kubviz/ketall.go b/agent/kubviz/plugins/ketall/ketall.go similarity index 96% rename from agent/kubviz/ketall.go rename to agent/kubviz/plugins/ketall/ketall.go index ffc2e7b4..8d91b6ab 100644 --- a/agent/kubviz/ketall.go +++ b/agent/kubviz/plugins/ketall/ketall.go @@ -1,8 +1,9 @@ -package main +package ketall import ( "context" "encoding/json" + "os" "time" "github.com/intelops/kubviz/constants" @@ -19,6 +20,8 @@ import ( "k8s.io/client-go/rest" ) +var ClusterName string = os.Getenv("CLUSTER_NAME") + func PublishAllResources(result model.Resource, js nats.JetStreamContext) error { metrics := result metrics.ClusterName = ClusterName @@ -33,12 +36,12 @@ func PublishAllResources(result model.Resource, js nats.JetStreamContext) error func GetAllResources(config *rest.Config, js nats.JetStreamContext) error { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("ketall") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "GetAllResources") span.SetAttributes(attribute.String("ketall-plugin-agent", "ketall-output")) defer span.End() - + // TODO: upto this uncomment for production // Create a new discovery client to discover all resources in the cluster dc := discovery.NewDiscoveryClientForConfigOrDie(config) diff --git a/agent/kubviz/kubePreUpgrade.go b/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go similarity index 99% rename from agent/kubviz/kubePreUpgrade.go rename to agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go index 68577ac0..4fb00b35 100644 --- a/agent/kubviz/kubePreUpgrade.go +++ b/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go @@ -1,4 +1,4 @@ -package main +package kubepreupgrade import ( "context" @@ -28,6 +28,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +var ClusterName string = os.Getenv("CLUSTER_NAME") + const ( baseURL = "https://raw.githubusercontent.com/kubernetes/kubernetes" fileURL = "api/openapi-spec/swagger.json" @@ -88,7 +90,7 @@ func KubePreUpgradeDetector(config *rest.Config, js nats.JetStreamContext) error _, span := tracer.Start(opentelemetry.BuildContext(ctx), "KubePreUpgradeDetector") span.SetAttributes(attribute.String("kubepug-plugin-agent", "kubepug-output")) defer span.End() - + pvcMountPath := "/mnt/agent/kbz" uniqueDir := fmt.Sprintf("%s/kubepug", pvcMountPath) err := os.MkdirAll(uniqueDir, 0755) diff --git a/agent/kubviz/kube_score.go b/agent/kubviz/plugins/kubescore/kube_score.go similarity index 92% rename from agent/kubviz/kube_score.go rename to agent/kubviz/plugins/kubescore/kube_score.go index 90551e0f..660aa175 100644 --- a/agent/kubviz/kube_score.go +++ b/agent/kubviz/plugins/kubescore/kube_score.go @@ -1,9 +1,10 @@ -package main +package kubescore import ( "context" "encoding/json" "log" + "os" exec "os/exec" "github.com/google/uuid" @@ -18,6 +19,8 @@ import ( "k8s.io/client-go/kubernetes" ) +var ClusterName string = os.Getenv("CLUSTER_NAME") + func RunKubeScore(clientset *kubernetes.Clientset, js nats.JetStreamContext) error { nsList, err := clientset.CoreV1(). Namespaces(). @@ -39,7 +42,7 @@ func publish(ns string, js nats.JetStreamContext) error { var report []json_v2.ScoredObject cmd := "kubectl api-resources --verbs=list --namespaced -o name | xargs -n1 -I{} sh -c \"kubectl get {} -n " + ns + " -oyaml && echo ---\" | kube-score score - -o json" log.Printf("Command: %#v,", cmd) - out, err := executeCommand(cmd) + out, err := ExecuteCommand(cmd) if err != nil { log.Println("Error occurred while running kube-score: ", err) return err @@ -60,13 +63,13 @@ func publish(ns string, js nats.JetStreamContext) error { } func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamContext) error { - - ctx:=context.Background() + + ctx := context.Background() tracer := otel.Tracer("kubescore") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "publishKubescoreMetrics") span.SetAttributes(attribute.String("kubescore-plugin-agent", "kubescore-output")) defer span.End() - + metrics := model.KubeScoreRecommendations{ ID: uuid.New().String(), ClusterName: ClusterName, @@ -82,9 +85,9 @@ func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamCon return nil } -func executeCommand(command string) (string, error) { +func ExecuteCommand(command string) (string, error) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("kubescore") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommand") span.SetAttributes(attribute.String("kubescore-agent", "kubescore-command-running")) diff --git a/agent/kubviz/outdated.go b/agent/kubviz/plugins/outdated/outdated.go similarity index 99% rename from agent/kubviz/outdated.go rename to agent/kubviz/plugins/outdated/outdated.go index 8b2fe748..975c510f 100644 --- a/agent/kubviz/outdated.go +++ b/agent/kubviz/plugins/outdated/outdated.go @@ -1,4 +1,4 @@ -package main +package outdated import ( "context" @@ -31,6 +31,8 @@ import ( "k8s.io/client-go/rest" ) +var ClusterName string = os.Getenv("CLUSTER_NAME") + const ( maxImageLength = 50 maxTagLength = 50 @@ -60,12 +62,12 @@ func truncateTagName(tagName string) string { } func PublishOutdatedImages(out model.CheckResultfinal, js nats.JetStreamContext) error { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("outdated-images") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishOutdatedImages") span.SetAttributes(attribute.String("outdated-plugin-agent", "outdated-output")) defer span.End() - + metrics := out metrics.ClusterName = ClusterName metricsJson, _ := json.Marshal(metrics) @@ -77,7 +79,7 @@ func PublishOutdatedImages(out model.CheckResultfinal, js nats.JetStreamContext) return nil } -func outDatedImages(config *rest.Config, js nats.JetStreamContext) error { +func OutDatedImages(config *rest.Config, js nats.JetStreamContext) error { images, err := ListImages(config) if err != nil { log.Println("unable to list images") diff --git a/agent/kubviz/rakees_agent.go b/agent/kubviz/plugins/rakkess/rakees_agent.go similarity index 77% rename from agent/kubviz/rakees_agent.go rename to agent/kubviz/plugins/rakkess/rakees_agent.go index 0b1d142c..93414db3 100644 --- a/agent/kubviz/rakees_agent.go +++ b/agent/kubviz/plugins/rakkess/rakees_agent.go @@ -1,4 +1,4 @@ -package main +package rakkess import ( "context" @@ -14,36 +14,37 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "github.com/intelops/kubviz/agent/kubviz/rakkess" "github.com/intelops/kubviz/model" "github.com/nats-io/nats.go" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) -func accessToOutcome(access rakkess.Access) (rakkess.Outcome, error) { +var ClusterName string = os.Getenv("CLUSTER_NAME") + +func accessToOutcome(access Access) (Outcome, error) { switch access { case 0: - return rakkess.None, nil + return None, nil case 1: - return rakkess.Up, nil + return Up, nil case 2: - return rakkess.Down, nil + return Down, nil case 3: - return rakkess.Err, nil + return Err, nil default: - return rakkess.None, fmt.Errorf("unknown access code: %d", access) + return None, fmt.Errorf("unknown access code: %d", access) } } func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("rakees") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RakeesOutput") span.SetAttributes(attribute.String("rakees-plugin-agent", "rakees-output")) defer span.End() - + // Create a new Kubernetes client client, err := kubernetes.NewForConfig(config) if err != nil { @@ -55,7 +56,7 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { if err != nil { return err } - var opts = rakkess.NewRakkessOptions() + var opts = NewRakkessOptions() opts.Verbs = []string{"list", "create", "update", "delete"} opts.OutputFormat = "icon-table" opts.ResourceList = resourceList @@ -63,7 +64,7 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { ctx, cancel := context.WithCancel(context.Background()) catchCtrlC(cancel) - res, err := rakkess.Resource(ctx, opts) + res, err := Resource(ctx, opts) if err != nil { fmt.Println("Error") return err @@ -89,10 +90,10 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { metrics := model.RakeesMetrics{ ClusterName: ClusterName, Name: resourceType, - Create: rakkess.HumanreadableAccessCode(createOutcome), - Delete: rakkess.HumanreadableAccessCode(deleteOutcome), - List: rakkess.HumanreadableAccessCode(listOutcome), - Update: rakkess.HumanreadableAccessCode(updateOutcome), + Create: HumanreadableAccessCode(createOutcome), + Delete: HumanreadableAccessCode(deleteOutcome), + List: HumanreadableAccessCode(listOutcome), + Update: HumanreadableAccessCode(updateOutcome), } metricsJson, _ := json.Marshal(metrics) _, err = js.Publish(constants.EventSubject_rakees, metricsJson) diff --git a/agent/kubviz/rakkess/rakkess.go b/agent/kubviz/plugins/rakkess/rakkess.go similarity index 100% rename from agent/kubviz/rakkess/rakkess.go rename to agent/kubviz/plugins/rakkess/rakkess.go diff --git a/agent/kubviz/trivy.go b/agent/kubviz/plugins/trivy/trivy.go similarity index 94% rename from agent/kubviz/trivy.go rename to agent/kubviz/plugins/trivy/trivy.go index 2cedf804..625b405a 100644 --- a/agent/kubviz/trivy.go +++ b/agent/kubviz/plugins/trivy/trivy.go @@ -1,4 +1,4 @@ -package main +package trivy import ( "bytes" @@ -20,6 +20,8 @@ import ( "go.opentelemetry.io/otel/attribute" ) +var ClusterName string = os.Getenv("CLUSTER_NAME") + func executeCommandTrivy(command string) ([]byte, error) { ctx := context.Background() @@ -27,7 +29,7 @@ func executeCommandTrivy(command string) ([]byte, error) { _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandTrivy") span.SetAttributes(attribute.String("trivy-k8s-agent", "command-running")) defer span.End() - + cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc @@ -56,7 +58,7 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivyK8sClusterScan") span.SetAttributes(attribute.String("cluster-name", report.ClusterName)) defer span.End() - + cmdString := fmt.Sprintf("trivy k8s --report summary cluster --exclude-nodes kubernetes.io/arch:amd64 --timeout 60m -f json --cache-dir %s --debug", trivyCacheDir) // clearCacheCmd := "trivy k8s --clear-cache" out, err := executeCommandTrivy(cmdString) @@ -85,14 +87,14 @@ func RunTrivyK8sClusterScan(js nats.JetStreamContext) error { // log.Printf("Error executing command: %v\n", err) // return err // } - err = publishTrivyK8sReport(report, js) + err = PublishTrivyK8sReport(report, js) if err != nil { return err } return nil } -func publishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamContext) error { +func PublishTrivyK8sReport(report report.ConsolidatedReport, js nats.JetStreamContext) error { metrics := model.Trivy{ ID: uuid.New().String(), ClusterName: ClusterName, diff --git a/agent/kubviz/trivy_image.go b/agent/kubviz/plugins/trivy/trivy_image.go similarity index 87% rename from agent/kubviz/trivy_image.go rename to agent/kubviz/plugins/trivy/trivy_image.go index d8d70e5a..c0acff33 100644 --- a/agent/kubviz/trivy_image.go +++ b/agent/kubviz/plugins/trivy/trivy_image.go @@ -1,4 +1,4 @@ -package main +package trivy import ( "context" @@ -10,6 +10,8 @@ import ( "github.com/aquasecurity/trivy/pkg/types" "github.com/google/uuid" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" "github.com/intelops/kubviz/pkg/opentelemetry" @@ -29,13 +31,13 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { } // clearCacheCmd := "trivy image --clear-cache" - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("trivy-image") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivyImageScans") span.SetAttributes(attribute.String("trivy-image-scan-agent", "image-scan")) defer span.End() - images, err := ListImages(config) + images, err := outdated.ListImages(config) if err != nil { log.Println("error occured while trying to list images, error :", err.Error()) return err @@ -44,7 +46,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { for _, image := range images { var report types.Report scanCmd := fmt.Sprintf("trivy image %s --timeout 60m -f json -q --cache-dir %s", image.PullableImage, trivyImageCacheDir) - out, err := executeCommand(scanCmd) + out, err := kubescore.ExecuteCommand(scanCmd) if err != nil { log.Printf("Error scanning image %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error @@ -71,7 +73,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { // log.Printf("Error executing command: %v\n", err) // return err // } - err = publishImageScanReports(report, js) + err = PublishImageScanReports(report, js) if err != nil { return err } @@ -79,7 +81,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { return nil } -func publishImageScanReports(report types.Report, js nats.JetStreamContext) error { +func PublishImageScanReports(report types.Report, js nats.JetStreamContext) error { metrics := model.TrivyImage{ ID: uuid.New().String(), ClusterName: ClusterName, diff --git a/agent/kubviz/trivy_sbom.go b/agent/kubviz/plugins/trivy/trivy_sbom.go similarity index 68% rename from agent/kubviz/trivy_sbom.go rename to agent/kubviz/plugins/trivy/trivy_sbom.go index 944037db..08358c32 100644 --- a/agent/kubviz/trivy_sbom.go +++ b/agent/kubviz/plugins/trivy/trivy_sbom.go @@ -1,4 +1,4 @@ -package main +package trivy import ( "bytes" @@ -11,6 +11,7 @@ import ( "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" "github.com/google/uuid" + "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" "github.com/intelops/kubviz/pkg/opentelemetry" @@ -20,45 +21,45 @@ import ( "k8s.io/client-go/rest" ) -func publishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { +func PublishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { - for _,packageinfo :=range report.Packages { + for _, packageinfo := range report.Packages { for _, pkg := range packageinfo.Packages { - metrics := model.SbomData{ - ID: uuid.New().String(), - ClusterName: ClusterName, - ComponentName: report.CycloneDX.Metadata.Component.Name, - PackageName: pkg.Name, - PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, - BomRef: report.CycloneDX.Metadata.Component.BOMRef, - SerialNumber: report.CycloneDX.SerialNumber, - CycloneDxVersion: report.CycloneDX.Version, - BomFormat: report.CycloneDX.BOMFormat, - } - metricsJson, err := json.Marshal(metrics) - if err!=nil { - log.Println("error occurred while marshalling sbom metrics in agent", err.Error()) - return err - } - _, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) - if err != nil { - return err + metrics := model.SbomData{ + ID: uuid.New().String(), + ClusterName: ClusterName, + ComponentName: report.CycloneDX.Metadata.Component.Name, + PackageName: pkg.Name, + PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, + BomRef: report.CycloneDX.Metadata.Component.BOMRef, + SerialNumber: report.CycloneDX.SerialNumber, + CycloneDxVersion: report.CycloneDX.Version, + BomFormat: report.CycloneDX.BOMFormat, + } + metricsJson, err := json.Marshal(metrics) + if err != nil { + log.Println("error occurred while marshalling sbom metrics in agent", err.Error()) + return err + } + _, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) + if err != nil { + return err + } + log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) + } } - log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) -} -} return nil } func executeCommandSbom(command string) ([]byte, error) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("trivy-sbom") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandSbom") span.SetAttributes(attribute.String("trivy-sbom-agent", "sbom-command-running")) defer span.End() - + cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc @@ -80,13 +81,13 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { return err } - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("trivy-sbom") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivySbomScan") span.SetAttributes(attribute.String("sbom", "sbom-creation")) defer span.End() - - images, err := ListImages(config) + + images, err := outdated.ListImages(config) if err != nil { log.Printf("failed to list images: %v", err) @@ -116,7 +117,7 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { log.Printf("Error unmarshaling JSON data for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error } - publishTrivySbomReport(report, js) + PublishTrivySbomReport(report, js) } return nil } diff --git a/agent/kubviz/scheduler.go b/agent/kubviz/scheduler.go deleted file mode 100644 index ce9b96d3..00000000 --- a/agent/kubviz/scheduler.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "sync" - - "github.com/pkg/errors" - "github.com/robfig/cron/v3" - - "github.com/intelops/go-common/logging" -) - -type jobHandler interface { - CronSpec() string - Run() -} - -type Scheduler struct { - log logging.Logger - jobs map[string]jobHandler - cronIDs map[string]cron.EntryID - c *cron.Cron - cronMutex *sync.Mutex -} - -func NewScheduler(log logging.Logger) *Scheduler { - clog := cron.VerbosePrintfLogger(log.(logging.StdLogger)) - return &Scheduler{ - log: log, - c: cron.New(cron.WithChain(cron.SkipIfStillRunning(clog), cron.Recover(clog))), - jobs: map[string]jobHandler{}, - cronIDs: map[string]cron.EntryID{}, - cronMutex: &sync.Mutex{}, - } -} - -func (t *Scheduler) AddJob(jobName string, job jobHandler) error { - t.cronMutex.Lock() - defer t.cronMutex.Unlock() - _, ok := t.cronIDs[jobName] - if ok { - return errors.Errorf("%s job already exists", jobName) - } - spec := job.CronSpec() - if spec == "" { - return errors.Errorf("%s job has no cron spec", jobName) - } - entryID, err := t.c.AddJob(spec, job) - if err != nil { - return errors.WithMessagef(err, "%s job cron spec not valid", jobName) - } - - t.jobs[jobName] = job - t.cronIDs[jobName] = entryID - t.log.Infof("%s job added with cron '%s'", jobName, spec) - return nil -} - -// RemoveJob ... -func (t *Scheduler) RemoveJob(jobName string) error { - t.cronMutex.Lock() - defer t.cronMutex.Unlock() - entryID, ok := t.cronIDs[jobName] - if !ok { - return errors.Errorf("%s job not exist", jobName) - } - - t.c.Remove(entryID) - delete(t.jobs, jobName) - delete(t.cronIDs, jobName) - t.log.Infof("%s job removed", jobName) - return nil -} - -func (t *Scheduler) Start() { - t.c.Start() - t.log.Infof("Job scheduler started") -} - -func (t *Scheduler) Stop() { - t.c.Stop() - t.log.Infof("Job scheduler stopped") -} - -func (t *Scheduler) GetJobs() map[string]jobHandler { - t.cronMutex.Lock() - defer t.cronMutex.Unlock() - return t.jobs -} diff --git a/agent/kubviz/scheduler/scheduler.go b/agent/kubviz/scheduler/scheduler.go new file mode 100644 index 00000000..f56660bc --- /dev/null +++ b/agent/kubviz/scheduler/scheduler.go @@ -0,0 +1,158 @@ +package scheduler + +import ( + "sync" + + "github.com/nats-io/nats.go" + "github.com/pkg/errors" + "github.com/robfig/cron/v3" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + + "github.com/intelops/go-common/logging" + "github.com/intelops/kubviz/agent/config" +) + +type jobHandler interface { + CronSpec() string + Run() +} + +type Scheduler struct { + log logging.Logger + jobs map[string]jobHandler + cronIDs map[string]cron.EntryID + c *cron.Cron + cronMutex *sync.Mutex +} + +func NewScheduler(log logging.Logger) *Scheduler { + clog := cron.VerbosePrintfLogger(log.(logging.StdLogger)) + return &Scheduler{ + log: log, + c: cron.New(cron.WithChain(cron.SkipIfStillRunning(clog), cron.Recover(clog))), + jobs: map[string]jobHandler{}, + cronIDs: map[string]cron.EntryID{}, + cronMutex: &sync.Mutex{}, + } +} + +func (t *Scheduler) AddJob(jobName string, job jobHandler) error { + t.cronMutex.Lock() + defer t.cronMutex.Unlock() + _, ok := t.cronIDs[jobName] + if ok { + return errors.Errorf("%s job already exists", jobName) + } + spec := job.CronSpec() + if spec == "" { + return errors.Errorf("%s job has no cron spec", jobName) + } + entryID, err := t.c.AddJob(spec, job) + if err != nil { + return errors.WithMessagef(err, "%s job cron spec not valid", jobName) + } + + t.jobs[jobName] = job + t.cronIDs[jobName] = entryID + t.log.Infof("%s job added with cron '%s'", jobName, spec) + return nil +} + +// RemoveJob ... +func (t *Scheduler) RemoveJob(jobName string) error { + t.cronMutex.Lock() + defer t.cronMutex.Unlock() + entryID, ok := t.cronIDs[jobName] + if !ok { + return errors.Errorf("%s job not exist", jobName) + } + + t.c.Remove(entryID) + delete(t.jobs, jobName) + delete(t.cronIDs, jobName) + t.log.Infof("%s job removed", jobName) + return nil +} + +func (t *Scheduler) Start() { + t.c.Start() + t.log.Infof("Job scheduler started") +} + +func (t *Scheduler) Stop() { + t.c.Stop() + t.log.Infof("Job scheduler stopped") +} + +func (t *Scheduler) GetJobs() map[string]jobHandler { + t.cronMutex.Lock() + defer t.cronMutex.Unlock() + return t.jobs +} + +func InitScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.AgentConfigurations, clientset *kubernetes.Clientset) (s *Scheduler) { + log := logging.NewLogger() + s = NewScheduler(log) + if cfg.OutdatedInterval != "" && cfg.OutdatedInterval != "0" { + sj, err := NewOutDatedImagesJob(config, js, cfg.OutdatedInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Outdated", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.GetAllInterval != "" && cfg.GetAllInterval != "0" { + sj, err := NewKetallJob(config, js, cfg.GetAllInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("GetALL", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.KubeScoreInterval != "" && cfg.KubeScoreInterval != "0" { + sj, err := NewKubescoreJob(clientset, js, cfg.KubeScoreInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("KubeScore", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.RakkessInterval != "" && cfg.RakkessInterval != "0" { + sj, err := NewRakkessJob(config, js, cfg.RakkessInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Rakkess", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.KubePreUpgradeInterval != "" && cfg.KubePreUpgradeInterval != "0" { + sj, err := NewKubePreUpgradeJob(config, js, cfg.KubePreUpgradeInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("KubePreUpgrade", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.TrivyInterval != "" && cfg.TrivyInterval != "0" { + sj, err := NewTrivyJob(config, js, cfg.TrivyInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Trivy", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + return +} diff --git a/agent/kubviz/scheduler_watch.go b/agent/kubviz/scheduler/scheduler_watch.go similarity index 75% rename from agent/kubviz/scheduler_watch.go rename to agent/kubviz/scheduler/scheduler_watch.go index 90f9f0dc..9b338cf7 100644 --- a/agent/kubviz/scheduler_watch.go +++ b/agent/kubviz/scheduler/scheduler_watch.go @@ -1,9 +1,17 @@ -package main +package scheduler import ( + "github.com/intelops/kubviz/agent/kubviz/plugins/events" + "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" "github.com/nats-io/nats.go" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + + "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" + "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" ) type OutDatedImagesJob struct { @@ -51,8 +59,8 @@ func (v *OutDatedImagesJob) CronSpec() string { func (j *OutDatedImagesJob) Run() { // Call the outDatedImages function with the provided config and js - err := outDatedImages(j.config, j.js) - LogErr(err) + err := outdated.OutDatedImages(j.config, j.js) + events.LogErr(err) } func NewKetallJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*KetallJob, error) { return &KetallJob{ @@ -67,8 +75,8 @@ func (v *KetallJob) CronSpec() string { func (j *KetallJob) Run() { // Call the Ketall function with the provided config and js - err := GetAllResources(j.config, j.js) - LogErr(err) + err := ketall.GetAllResources(j.config, j.js) + events.LogErr(err) } func NewKubePreUpgradeJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*KubePreUpgradeJob, error) { @@ -84,8 +92,8 @@ func (v *KubePreUpgradeJob) CronSpec() string { func (j *KubePreUpgradeJob) Run() { // Call the Kubepreupgrade function with the provided config and js - err := KubePreUpgradeDetector(j.config, j.js) - LogErr(err) + err := kubepreupgrade.KubePreUpgradeDetector(j.config, j.js) + events.LogErr(err) } func NewKubescoreJob(clientset *kubernetes.Clientset, js nats.JetStreamContext, frequency string) (*KubescoreJob, error) { @@ -101,8 +109,8 @@ func (v *KubescoreJob) CronSpec() string { func (j *KubescoreJob) Run() { // Call the Kubescore function with the provided config and js - err := RunKubeScore(j.clientset, j.js) - LogErr(err) + err := kubescore.RunKubeScore(j.clientset, j.js) + events.LogErr(err) } func NewRakkessJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*RakkessJob, error) { return &RakkessJob{ @@ -117,8 +125,8 @@ func (v *RakkessJob) CronSpec() string { func (j *RakkessJob) Run() { // Call the Rakkes function with the provided config and js - err := RakeesOutput(j.config, j.js) - LogErr(err) + err := rakkess.RakeesOutput(j.config, j.js) + events.LogErr(err) } func NewTrivyJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivyJob, error) { return &TrivyJob{ @@ -133,10 +141,10 @@ func (v *TrivyJob) CronSpec() string { func (j *TrivyJob) Run() { // Call the Trivy function with the provided config and js - err := RunTrivySbomScan(j.config, j.js) - LogErr(err) - err = RunTrivyImageScans(j.config, j.js) - LogErr(err) - err = RunTrivyK8sClusterScan(j.js) - LogErr(err) + err := trivy.RunTrivySbomScan(j.config, j.js) + events.LogErr(err) + err = trivy.RunTrivyImageScans(j.config, j.js) + events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(j.js) + events.LogErr(err) } From 8a3991ad9f26bc95c40b1b9885ab458f524cf27e Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 9 Feb 2024 16:52:37 +0530 Subject: [PATCH 197/263] imagename --- agent/kubviz/k8smetrics_agent.go | 52 ++++++++++++++++++++++++++--- client/pkg/clickhouse/db_client.go | 1 + client/pkg/clickhouse/statements.go | 2 +- model/metrics.go | 1 + sql/000001_events.up.sql | 1 + 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 4fed67fa..62cfb0c4 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -11,6 +11,7 @@ import ( "time" "github.com/intelops/go-common/logging" + "github.com/pkg/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -205,7 +206,7 @@ func publishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, e errCh <- nil } -func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext) (bool, error) { +func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext, imageName string) (bool, error) { ctx := context.Background() tracer := otel.Tracer("kubviz-publish-k8smetrics") @@ -218,6 +219,7 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre Type: mtype, Event: mdata, ClusterName: ClusterName, + ImageName: imageName, } metricsJson, _ := json.Marshal(metrics) _, err := js.Publish(constants.EventSubject, metricsJson) @@ -228,6 +230,27 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre return false, nil } +func getK8sPodImages(clientset *kubernetes.Clientset, namespace, podName string) ([]string, error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + pod, err := clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + var images []string + for _, container := range pod.Spec.Containers { + images = append(images, container.Image) + } + + if len(images) == 0 { + return nil, errors.New("no containers found in the pod") + } + + return images, nil +} + // createStream creates a stream by using JetStreamContext func createStream(js nats.JetStreamContext) error { // Check if the METRICS stream already exists; if not, create it. @@ -327,15 +350,36 @@ func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { event := obj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js) + images, err := getK8sPodImages(clientset, event.InvolvedObject.Namespace, event.InvolvedObject.Name) + if err != nil { + log.Println("Error retrieving image names:", err) + return + } + for _, image := range images { + publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js, image) + } }, DeleteFunc: func(obj interface{}) { event := obj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js) + images, err := getK8sPodImages(clientset, event.InvolvedObject.Namespace, event.InvolvedObject.Name) + if err != nil { + log.Println("Error retrieving image names:", err) + return + } + for _, image := range images { + publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js, image) + } }, UpdateFunc: func(oldObj, newObj interface{}) { event := newObj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js) + images, err := getK8sPodImages(clientset, event.InvolvedObject.Namespace, event.InvolvedObject.Name) + if err != nil { + log.Println("Error retrieving image names:", err) + return + } + for _, image := range images { + publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js, image) + } }, }, ) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 8e8a6fe7..b0e279e9 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -543,6 +543,7 @@ func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { metrics.Event.Reason, metrics.Event.Source.Host, string(eventJson), + metrics.ImageName, formattedFirstTimestamp, formattedLastTimestamp, ); err != nil { diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index c248aab3..19e0c377 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -222,7 +222,7 @@ const InsertKetall DBStatement = "INSERT INTO getall_resources (ClusterName, Nam const InsertOutdated DBStatement = "INSERT INTO outdated_images (ClusterName, Namespace, Pod, CurrentImage, CurrentTag, LatestVersion, VersionsBehind, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertDepricatedApi DBStatement = "INSERT INTO DeprecatedAPIs (ClusterName, ObjectName, Description, Kind, Deprecated, Scope, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?)" const InsertDeletedApi DBStatement = "INSERT INTO DeletedAPIs (ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertKubvizEvent DBStatement = "INSERT INTO events (ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, FirstTime, LastTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertKubvizEvent DBStatement = "INSERT INTO events (ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, ImageName, FirstTime, LastTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const clickhouseExperimental DBStatement = `SET allow_experimental_object_type=1;` const containerGithubTable DBStatement = `CREATE table IF NOT EXISTS container_github(event JSON) ENGINE = MergeTree ORDER BY tuple();` const InsertKubeScore string = "INSERT INTO kubescore(id,clustername,object_name,kind,apiVersion,name,namespace,target_type,description,path,summary,file_name,file_row,EventTime) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)" diff --git a/model/metrics.go b/model/metrics.go index 9a462ef1..6e339720 100644 --- a/model/metrics.go +++ b/model/metrics.go @@ -7,4 +7,5 @@ type Metrics struct { Type string Event *v1.Event ClusterName string + ImageName string } diff --git a/sql/000001_events.up.sql b/sql/000001_events.up.sql index a410aaf5..2ba1313f 100644 --- a/sql/000001_events.up.sql +++ b/sql/000001_events.up.sql @@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS events ( Reason String, Host String, Event String, + ImageName String, FirstTime String, LastTime String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} From 224017a1af87d7f4236ff5dc965f03774018a95c Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 9 Feb 2024 16:54:35 +0530 Subject: [PATCH 198/263] added kuberhealthy agent part --- agent/config/config.go | 16 +++ agent/kubviz/k8smetrics_agent.go | 7 +- .../plugins/kuberhealthy/kuberhealthy.go | 98 +++++++++++++++++++ constants/constants.go | 1 + model/kuberhealthy.go | 39 ++++++++ 5 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 agent/kubviz/plugins/kuberhealthy/kuberhealthy.go create mode 100644 model/kuberhealthy.go diff --git a/agent/config/config.go b/agent/config/config.go index 73fddddf..632b3332 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -1,6 +1,8 @@ package config import ( + "time" + "github.com/kelseyhightower/envconfig" "github.com/pkg/errors" ) @@ -15,6 +17,7 @@ type AgentConfigurations struct { KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` TrivyInterval string `envconfig:"TRIVY_INTERVAL" default:"*/10 * * * *"` SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"true"` + KuberHealthyEnable bool `envconfig:"KUBERHEALTHY_ENABLE" default:"true"` } func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { @@ -24,3 +27,16 @@ func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { } return } + +type KHConfig struct { + KuberhealthyURL string `envconfig:"KUBERHEALTHY_URL" required:"true"` + PollInterval time.Duration `envconfig:"POLL_INTERVAL" default:"15m"` +} + +func GetKuberHealthyConfig() (khconfig *KHConfig, err error) { + khconfig = &KHConfig{} + if err = envconfig.Process("", khconfig); err != nil { + return nil, errors.WithStack(err) + } + return +} diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index b99ddd91..76ae1926 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -23,6 +23,7 @@ import ( "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" @@ -52,8 +53,7 @@ const ( var ( ClusterName string = os.Getenv("CLUSTER_NAME") token string = os.Getenv("NATS_TOKEN") - - natsurl string = os.Getenv("NATS_ADDRESS") + natsurl string = os.Getenv("NATS_ADDRESS") //for local testing provide the location of kubeconfig cluster_conf_loc string = os.Getenv("CONFIG_LOCATION") @@ -128,6 +128,9 @@ func main() { }() go events.PublishMetrics(clientset, js, clusterMetricsChan) + if cfg.KuberHealthyEnable { + go kuberhealthy.StartKuberHealthy(js) + } go server.StartServer() collectAndPublishMetrics := func() { err := outdated.OutDatedImages(config, js) diff --git a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go new file mode 100644 index 00000000..7714ba35 --- /dev/null +++ b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go @@ -0,0 +1,98 @@ +package kuberhealthy + +import ( + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "strings" + "time" + + "github.com/intelops/kubviz/agent/config" + "github.com/intelops/kubviz/constants" + "github.com/intelops/kubviz/model" + "github.com/intelops/kubviz/pkg/opentelemetry" + "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" +) + +func StartKuberHealthy(js nats.JetStreamContext) { + khConfig, err := config.GetKuberHealthyConfig() + if err != nil { + log.Fatalf("Error getting Kuberhealthy config: %v", err) + } + + ticker := time.NewTicker(khConfig.PollInterval) + defer ticker.Stop() + + for range ticker.C { + if err := pollAndPublishKuberhealthy(khConfig.KuberhealthyURL, js); err != nil { + log.Printf("Error polling and publishing Kuberhealthy metrics: %v", err) + } + } +} +func pollAndPublishKuberhealthy(url string, js nats.JetStreamContext) error { + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("error making GET request to Kuberhealthy: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("error reading response body: %w", err) + } + + var state model.State + if err := json.Unmarshal(body, &state); err != nil { + return fmt.Errorf("error unmarshaling response: %w", err) + } + + return PublishKuberhealthyMetrics(js, state) +} +func boolToUInt8(b bool) uint8 { + if b { + return 1 + } + return 0 +} + +func errorsToString(errors []string) string { + return strings.Join(errors, ", ") +} +func PublishKuberhealthyMetrics(js nats.JetStreamContext, state model.State) error { + ctx := context.Background() + tracer := otel.Tracer("kuberhealthy") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishKuberhealthyMetrics") + defer span.End() + + for checkName, details := range state.CheckDetails { + metrics := model.KuberhealthyCheckDetail{ + CurrentUUID: details.CurrentUUID, + CheckName: checkName, + OK: boolToUInt8(details.OK), + Errors: errorsToString(details.Errors), + RunDuration: details.RunDuration, + Namespace: details.Namespace, + Node: details.Node, + LastRun: details.LastRun.Time, + AuthoritativePod: details.AuthoritativePod, + } + + metricsJSON, err := json.Marshal(metrics) + if err != nil { + log.Printf("Error marshaling metrics of kuberhealthy %s: %v", checkName, err) + continue + } + + if _, err := js.Publish(constants.KUBERHEALTHY_SUBJECT, metricsJSON); err != nil { + log.Printf("Error publishing metrics for kuberhealthy %s: %v", checkName, err) + continue + } + } + + log.Printf("Kuberhealthy metrics have been published") + return nil +} diff --git a/constants/constants.go b/constants/constants.go index ba5aa050..6dbffaaa 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -1,6 +1,7 @@ package constants const ( + KUBERHEALTHY_SUBJECT = "METRICS.kuberhealthy" KUBESCORE_SUBJECT = "METRICS.kubescore" TRIVY_K8S_SUBJECT = "METRICS.trivyk8s" StreamSubjects = "METRICS.*" diff --git a/model/kuberhealthy.go b/model/kuberhealthy.go new file mode 100644 index 00000000..76c84d0e --- /dev/null +++ b/model/kuberhealthy.go @@ -0,0 +1,39 @@ +package model + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type State struct { + OK bool `json:"OK"` + Errors []string `json:"Errors"` + CheckDetails map[string]WorkloadDetails `json:"CheckDetails"` // map of check names to last run timestamp + JobDetails map[string]WorkloadDetails `json:"JobDetails"` // map of job names to last run timestamp + CurrentMaster string `json:"CurrentMaster"` + Metadata map[string]string `json:"Metadata"` +} + +type WorkloadDetails struct { + OK bool `json:"OK" yaml:"OK"` // true or false status of the khWorkload, whether or not it completed successfully + Errors []string `json:"Errors" yaml:"Errors"` // the list of errors reported from the khWorkload run + RunDuration string `json:"RunDuration" yaml:"RunDuration"` // the time it took for the khWorkload to complete + Namespace string `json:"Namespace" yaml:"Namespace"` // the namespace the khWorkload was run in + Node string `json:"Node" yaml:"Node"` // the node the khWorkload ran on + LastRun *metav1.Time `json:"LastRun,omitempty" yaml:"LastRun,omitempty"` // the time the khWorkload was last run + AuthoritativePod string `json:"AuthoritativePod" yaml:"AuthoritativePod"` // the main kuberhealthy pod creating and updating the khstate + CurrentUUID string `json:"uuid" yaml:"uuid"` // the UUID that is authorized to report statuses into the kuberhealthy endpoint +} + +type KuberhealthyCheckDetail struct { + CurrentUUID string `json:"currentUUID"` + CheckName string `json:"checkName"` + OK uint8 `json:"ok"` + Errors string `json:"errors"` + RunDuration string `json:"runDuration"` + Namespace string `json:"namespace"` + Node string `json:"node"` + LastRun time.Time `json:"lastRun"` + AuthoritativePod string `json:"authoritativePod"` +} From 0b748071a6df96b58d25900a7e47b2178707261f Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 12 Feb 2024 13:26:34 +0530 Subject: [PATCH 199/263] test --- steps-to-test.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/steps-to-test.txt b/steps-to-test.txt index 8aee4a19..6e2b1af7 100644 --- a/steps-to-test.txt +++ b/steps-to-test.txt @@ -133,3 +133,5 @@ query { ] } } + + ... From 48615a5356ad9ab72530cb3e45153e27ee0e2ea1 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 12 Feb 2024 13:32:54 +0530 Subject: [PATCH 200/263] added count handlers in graphql --- graphqlserver/graph/generated.go | 734 +++++++++++++++++++++++- graphqlserver/graph/model/models_gen.go | 11 + graphqlserver/graph/schema.graphqls | 13 + graphqlserver/graph/schema.resolvers.go | 100 ++++ 4 files changed, 847 insertions(+), 11 deletions(-) diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index ba3f13df..a0419d01 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -46,12 +46,23 @@ type DirectiveRoot struct { } type ComplexityRoot struct { + ClusterAPIsCount struct { + ClusterName func(childComplexity int) int + Count func(childComplexity int) int + } + ClusterNamespaceOutdatedCount struct { ClusterName func(childComplexity int) int Namespace func(childComplexity int) int OutdatedCount func(childComplexity int) int } + ClusterNamespaceResourceCount struct { + ClusterName func(childComplexity int) int + Namespace func(childComplexity int) int + ResourceCount func(childComplexity int) int + } + DeletedAPI struct { ClusterName func(childComplexity int) int Deleted func(childComplexity int) int @@ -157,7 +168,10 @@ type ComplexityRoot struct { } Query struct { + AllClusterDeletedAPIsCounts func(childComplexity int) int + AllClusterDeprecatedAPIsCounts func(childComplexity int) int AllClusterNamespaceOutdatedCounts func(childComplexity int) int + AllClusterNamespaceResourceCounts func(childComplexity int) int AllDeletedAPIs func(childComplexity int) int AllDeprecatedAPIs func(childComplexity int) int AllEvents func(childComplexity int) int @@ -282,6 +296,9 @@ type QueryResolver interface { OutdatedImagesByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.OutdatedImage, error) OutdatedImagesCount(ctx context.Context, clusterName string, namespace string) (int, error) AllClusterNamespaceOutdatedCounts(ctx context.Context) ([]*model.ClusterNamespaceOutdatedCount, error) + AllClusterDeprecatedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) + AllClusterDeletedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) + AllClusterNamespaceResourceCounts(ctx context.Context) ([]*model.ClusterNamespaceResourceCount, error) } type executableSchema struct { @@ -303,6 +320,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "ClusterAPIsCount.clusterName": + if e.complexity.ClusterAPIsCount.ClusterName == nil { + break + } + + return e.complexity.ClusterAPIsCount.ClusterName(childComplexity), true + + case "ClusterAPIsCount.count": + if e.complexity.ClusterAPIsCount.Count == nil { + break + } + + return e.complexity.ClusterAPIsCount.Count(childComplexity), true + case "ClusterNamespaceOutdatedCount.clusterName": if e.complexity.ClusterNamespaceOutdatedCount.ClusterName == nil { break @@ -324,6 +355,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ClusterNamespaceOutdatedCount.OutdatedCount(childComplexity), true + case "ClusterNamespaceResourceCount.clusterName": + if e.complexity.ClusterNamespaceResourceCount.ClusterName == nil { + break + } + + return e.complexity.ClusterNamespaceResourceCount.ClusterName(childComplexity), true + + case "ClusterNamespaceResourceCount.namespace": + if e.complexity.ClusterNamespaceResourceCount.Namespace == nil { + break + } + + return e.complexity.ClusterNamespaceResourceCount.Namespace(childComplexity), true + + case "ClusterNamespaceResourceCount.resourceCount": + if e.complexity.ClusterNamespaceResourceCount.ResourceCount == nil { + break + } + + return e.complexity.ClusterNamespaceResourceCount.ResourceCount(childComplexity), true + case "DeletedAPI.ClusterName": if e.complexity.DeletedAPI.ClusterName == nil { break @@ -884,6 +936,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.OutdatedImage.VersionsBehind(childComplexity), true + case "Query.allClusterDeletedAPIsCounts": + if e.complexity.Query.AllClusterDeletedAPIsCounts == nil { + break + } + + return e.complexity.Query.AllClusterDeletedAPIsCounts(childComplexity), true + + case "Query.allClusterDeprecatedAPIsCounts": + if e.complexity.Query.AllClusterDeprecatedAPIsCounts == nil { + break + } + + return e.complexity.Query.AllClusterDeprecatedAPIsCounts(childComplexity), true + case "Query.allClusterNamespaceOutdatedCounts": if e.complexity.Query.AllClusterNamespaceOutdatedCounts == nil { break @@ -891,6 +957,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.AllClusterNamespaceOutdatedCounts(childComplexity), true + case "Query.allClusterNamespaceResourceCounts": + if e.complexity.Query.AllClusterNamespaceResourceCounts == nil { + break + } + + return e.complexity.Query.AllClusterNamespaceResourceCounts(childComplexity), true + case "Query.allDeletedAPIs": if e.complexity.Query.AllDeletedAPIs == nil { break @@ -1712,6 +1785,94 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** +func (ec *executionContext) _ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterAPIsCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ClusterAPIsCount_count(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterAPIsCount_count(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Count, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterAPIsCount_count(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterAPIsCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) if err != nil { @@ -1844,6 +2005,138 @@ func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_outdatedC return fc, nil } +func (ec *executionContext) _ClusterNamespaceResourceCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceResourceCount_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterNamespaceResourceCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ClusterNamespaceResourceCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceResourceCount_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterNamespaceResourceCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ClusterNamespaceResourceCount_resourceCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ResourceCount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterNamespaceResourceCount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DeletedAPI_ClusterName(ctx, field) if err != nil { @@ -6235,8 +6528,160 @@ func (ec *executionContext) fieldContext_Query_outdatedImagesCount(ctx context.C return fc, nil } -func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx, field) +func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterNamespaceOutdatedCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterNamespaceOutdatedCount) + fc.Result = res + return ec.marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) + case "outdatedCount": + return ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceOutdatedCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterDeprecatedAPIsCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterDeprecatedAPIsCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterDeprecatedAPIsCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterAPIsCount) + fc.Result = res + return ec.marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterDeprecatedAPIsCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) + case "count": + return ec.fieldContext_ClusterAPIsCount_count(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterAPIsCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterDeletedAPIsCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterDeletedAPIsCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterDeletedAPIsCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterAPIsCount) + fc.Result = res + return ec.marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterDeletedAPIsCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) + case "count": + return ec.fieldContext_ClusterAPIsCount_count(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterAPIsCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterNamespaceResourceCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterNamespaceResourceCounts(ctx, field) if err != nil { return graphql.Null } @@ -6249,7 +6694,7 @@ func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllClusterNamespaceOutdatedCounts(rctx) + return ec.resolvers.Query().AllClusterNamespaceResourceCounts(rctx) }) if err != nil { ec.Error(ctx, err) @@ -6261,12 +6706,12 @@ func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context } return graphql.Null } - res := resTmp.([]*model.ClusterNamespaceOutdatedCount) + res := resTmp.([]*model.ClusterNamespaceResourceCount) fc.Result = res - return ec.marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx, field.Selections, res) + return ec.marshalNClusterNamespaceResourceCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceResourceCountᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_allClusterNamespaceResourceCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -6275,13 +6720,13 @@ func (ec *executionContext) fieldContext_Query_allClusterNamespaceOutdatedCounts Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "clusterName": - return ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) + return ec.fieldContext_ClusterNamespaceResourceCount_clusterName(ctx, field) case "namespace": - return ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) - case "outdatedCount": - return ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) + return ec.fieldContext_ClusterNamespaceResourceCount_namespace(ctx, field) + case "resourceCount": + return ec.fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceOutdatedCount", field.Name) + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceResourceCount", field.Name) }, } return fc, nil @@ -11138,6 +11583,50 @@ func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Conte // region **************************** object.gotpl **************************** +var clusterAPIsCountImplementors = []string{"ClusterAPIsCount"} + +func (ec *executionContext) _ClusterAPIsCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterAPIsCount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterAPIsCountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterAPIsCount") + case "clusterName": + out.Values[i] = ec._ClusterAPIsCount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "count": + out.Values[i] = ec._ClusterAPIsCount_count(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var clusterNamespaceOutdatedCountImplementors = []string{"ClusterNamespaceOutdatedCount"} func (ec *executionContext) _ClusterNamespaceOutdatedCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceOutdatedCount) graphql.Marshaler { @@ -11187,6 +11676,55 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount(ctx context.Context, return out } +var clusterNamespaceResourceCountImplementors = []string{"ClusterNamespaceResourceCount"} + +func (ec *executionContext) _ClusterNamespaceResourceCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceResourceCount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterNamespaceResourceCountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterNamespaceResourceCount") + case "clusterName": + out.Values[i] = ec._ClusterNamespaceResourceCount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._ClusterNamespaceResourceCount_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceCount": + out.Values[i] = ec._ClusterNamespaceResourceCount_resourceCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var deletedAPIImplementors = []string{"DeletedAPI"} func (ec *executionContext) _DeletedAPI(ctx context.Context, sel ast.SelectionSet, obj *model.DeletedAPI) graphql.Marshaler { @@ -12070,6 +12608,72 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterDeprecatedAPIsCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterDeprecatedAPIsCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterDeletedAPIsCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterDeletedAPIsCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterNamespaceResourceCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterNamespaceResourceCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -12819,6 +13423,60 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.ClusterAPIsCount) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNClusterAPIsCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCount(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNClusterAPIsCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterAPIsCount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterAPIsCount(ctx, sel, v) +} + func (ec *executionContext) marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.ClusterNamespaceOutdatedCount) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -12873,6 +13531,60 @@ func (ec *executionContext) marshalNClusterNamespaceOutdatedCount2ᚖgithubᚗco return ec._ClusterNamespaceOutdatedCount(ctx, sel, v) } +func (ec *executionContext) marshalNClusterNamespaceResourceCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceResourceCountᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.ClusterNamespaceResourceCount) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNClusterNamespaceResourceCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceResourceCount(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNClusterNamespaceResourceCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceResourceCount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterNamespaceResourceCount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterNamespaceResourceCount(ctx, sel, v) +} + func (ec *executionContext) marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.DeletedAPI) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index 3f28f8bc..397b571e 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -2,12 +2,23 @@ package model +type ClusterAPIsCount struct { + ClusterName string `json:"clusterName"` + Count int `json:"count"` +} + type ClusterNamespaceOutdatedCount struct { ClusterName string `json:"clusterName"` Namespace string `json:"namespace"` OutdatedCount int `json:"outdatedCount"` } +type ClusterNamespaceResourceCount struct { + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + ResourceCount int `json:"resourceCount"` +} + type DeletedAPI struct { ClusterName *string `json:"ClusterName,omitempty"` ObjectName *string `json:"ObjectName,omitempty"` diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index dc755361..72ef18ef 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -15,7 +15,20 @@ type Query { outdatedImagesByClusterAndNamespace(clusterName: String!, namespace: String!): [OutdatedImage!]! outdatedImagesCount(clusterName: String!, namespace: String!): Int! allClusterNamespaceOutdatedCounts: [ClusterNamespaceOutdatedCount!]! + allClusterDeprecatedAPIsCounts: [ClusterAPIsCount!]! + allClusterDeletedAPIsCounts: [ClusterAPIsCount!]! + allClusterNamespaceResourceCounts: [ClusterNamespaceResourceCount!]! } +type ClusterNamespaceResourceCount { + clusterName: String! + namespace: String! + resourceCount: Int! +} +type ClusterAPIsCount { + clusterName: String! + count: Int! +} + type ClusterNamespaceOutdatedCount { clusterName: String! diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index e58892de..296b70e4 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -524,6 +524,106 @@ func (r *queryResolver) AllClusterNamespaceOutdatedCounts(ctx context.Context) ( return results, nil } +// AllClusterDeprecatedAPIsCounts is the resolver for the allClusterDeprecatedAPIsCounts field. +func (r *queryResolver) AllClusterDeprecatedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + query := ` + SELECT ClusterName, COUNT(*) as count + FROM DeprecatedAPIs + GROUP BY ClusterName +` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + var results []*model.ClusterAPIsCount + for rows.Next() { + var result model.ClusterAPIsCount + if err := rows.Scan(&result.ClusterName, &result.Count); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + results = append(results, &result) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return results, nil +} + +// AllClusterDeletedAPIsCounts is the resolver for the allClusterDeletedAPIsCounts field. +func (r *queryResolver) AllClusterDeletedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := ` + SELECT ClusterName, COUNT(*) as count + FROM DeletedAPIs + GROUP BY ClusterName + ` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var results []*model.ClusterAPIsCount + for rows.Next() { + var result model.ClusterAPIsCount + if err := rows.Scan(&result.ClusterName, &result.Count); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + results = append(results, &result) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return results, nil +} + +// AllClusterNamespaceResourceCounts is the resolver for the allClusterNamespaceResourceCounts field. +func (r *queryResolver) AllClusterNamespaceResourceCounts(ctx context.Context) ([]*model.ClusterNamespaceResourceCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + query := ` + SELECT ClusterName, Namespace, COUNT(*) as resourceCount + FROM getall_resources + GROUP BY ClusterName, Namespace + ` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var results []*model.ClusterNamespaceResourceCount + for rows.Next() { + var result model.ClusterNamespaceResourceCount + if err := rows.Scan(&result.ClusterName, &result.Namespace, &result.ResourceCount); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + results = append(results, &result) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return results, nil +} + // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } From f846babc7643d401bde80d16f90b8bb3ba5542e2 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 13 Feb 2024 17:15:23 +0530 Subject: [PATCH 201/263] changed unique namespace and unique clusters into objects --- graphqlserver/graph/generated.go | 376 ++++++++++++++++++++---- graphqlserver/graph/model/models_gen.go | 8 + graphqlserver/graph/schema.graphqls | 13 +- graphqlserver/graph/schema.resolvers.go | 44 ++- graphqlserver/graph/utils.go | 28 ++ 5 files changed, 391 insertions(+), 78 deletions(-) diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index a0419d01..6916e996 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -46,6 +46,10 @@ type DirectiveRoot struct { } type ComplexityRoot struct { + Cluster struct { + Name func(childComplexity int) int + } + ClusterAPIsCount struct { ClusterName func(childComplexity int) int Count func(childComplexity int) int @@ -149,6 +153,10 @@ type ComplexityRoot struct { TargetType func(childComplexity int) int } + Namespace struct { + Name func(childComplexity int) int + } + NamespaceData struct { KubeScores func(childComplexity int) int Namespace func(childComplexity int) int @@ -291,8 +299,8 @@ type QueryResolver interface { AllKubeScores(ctx context.Context) ([]*model.Kubescore, error) AllTrivyVuls(ctx context.Context) ([]*model.TrivyVul, error) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyMisconfig, error) - UniqueClusters(ctx context.Context) ([]string, error) - UniqueNamespaces(ctx context.Context) ([]string, error) + UniqueNamespaces(ctx context.Context) ([]*model.Namespace, error) + UniqueClusters(ctx context.Context) ([]*model.Cluster, error) OutdatedImagesByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.OutdatedImage, error) OutdatedImagesCount(ctx context.Context, clusterName string, namespace string) (int, error) AllClusterNamespaceOutdatedCounts(ctx context.Context) ([]*model.ClusterNamespaceOutdatedCount, error) @@ -320,6 +328,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "Cluster.name": + if e.complexity.Cluster.Name == nil { + break + } + + return e.complexity.Cluster.Name(childComplexity), true + case "ClusterAPIsCount.clusterName": if e.complexity.ClusterAPIsCount.ClusterName == nil { break @@ -852,6 +867,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Kubescore.TargetType(childComplexity), true + case "Namespace.name": + if e.complexity.Namespace.Name == nil { + break + } + + return e.complexity.Namespace.Name(childComplexity), true + case "NamespaceData.kubeScores": if e.complexity.NamespaceData.KubeScores == nil { break @@ -1785,6 +1807,50 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** +func (ec *executionContext) _Cluster_name(ctx context.Context, field graphql.CollectedField, obj *model.Cluster) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Cluster_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Cluster_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Cluster", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) if err != nil { @@ -4970,6 +5036,50 @@ func (ec *executionContext) fieldContext_Kubescore_expiryDate(ctx context.Contex return fc, nil } +func (ec *executionContext) _Namespace_name(ctx context.Context, field graphql.CollectedField, obj *model.Namespace) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Namespace_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Namespace_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Namespace", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { fc, err := ec.fieldContext_NamespaceData_namespace(ctx, field) if err != nil { @@ -6312,8 +6422,8 @@ func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Co return fc, nil } -func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_uniqueClusters(ctx, field) +func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_uniqueNamespaces(ctx, field) if err != nil { return graphql.Null } @@ -6326,7 +6436,7 @@ func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().UniqueClusters(rctx) + return ec.resolvers.Query().UniqueNamespaces(rctx) }) if err != nil { ec.Error(ctx, err) @@ -6338,26 +6448,30 @@ func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field gra } return graphql.Null } - res := resTmp.([]string) + res := resTmp.([]*model.Namespace) fc.Result = res - return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNNamespace2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "name": + return ec.fieldContext_Namespace_name(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Namespace", field.Name) }, } return fc, nil } -func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_uniqueNamespaces(ctx, field) +func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_uniqueClusters(ctx, field) if err != nil { return graphql.Null } @@ -6370,7 +6484,7 @@ func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().UniqueNamespaces(rctx) + return ec.resolvers.Query().UniqueClusters(rctx) }) if err != nil { ec.Error(ctx, err) @@ -6382,19 +6496,23 @@ func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field g } return graphql.Null } - res := resTmp.([]string) + res := resTmp.([]*model.Cluster) fc.Result = res - return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) + return ec.marshalNCluster2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "name": + return ec.fieldContext_Cluster_name(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Cluster", field.Name) }, } return fc, nil @@ -11583,6 +11701,45 @@ func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Conte // region **************************** object.gotpl **************************** +var clusterImplementors = []string{"Cluster"} + +func (ec *executionContext) _Cluster(ctx context.Context, sel ast.SelectionSet, obj *model.Cluster) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Cluster") + case "name": + out.Values[i] = ec._Cluster_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var clusterAPIsCountImplementors = []string{"ClusterAPIsCount"} func (ec *executionContext) _ClusterAPIsCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterAPIsCount) graphql.Marshaler { @@ -12110,6 +12267,45 @@ func (ec *executionContext) _Kubescore(ctx context.Context, sel ast.SelectionSet return out } +var namespaceImplementors = []string{"Namespace"} + +func (ec *executionContext) _Namespace(ctx context.Context, sel ast.SelectionSet, obj *model.Namespace) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, namespaceImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Namespace") + case "name": + out.Values[i] = ec._Namespace_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var namespaceDataImplementors = []string{"NamespaceData"} func (ec *executionContext) _NamespaceData(ctx context.Context, sel ast.SelectionSet, obj *model.NamespaceData) graphql.Marshaler { @@ -12499,7 +12695,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "uniqueClusters": + case "uniqueNamespaces": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12508,7 +12704,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_uniqueClusters(ctx, field) + res = ec._Query_uniqueNamespaces(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12521,7 +12717,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "uniqueNamespaces": + case "uniqueClusters": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12530,7 +12726,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_uniqueNamespaces(ctx, field) + res = ec._Query_uniqueClusters(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -13423,6 +13619,60 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) marshalNCluster2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Cluster) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNCluster2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐCluster(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNCluster2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐCluster(ctx context.Context, sel ast.SelectionSet, v *model.Cluster) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Cluster(ctx, sel, v) +} + func (ec *executionContext) marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.ClusterAPIsCount) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -13939,6 +14189,60 @@ func (ec *executionContext) marshalNKubescore2ᚖgithubᚗcomᚋintelopsᚋkubvi return ec._Kubescore(ctx, sel, v) } +func (ec *executionContext) marshalNNamespace2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Namespace) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNNamespace2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespace(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNNamespace2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespace(ctx context.Context, sel ast.SelectionSet, v *model.Namespace) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Namespace(ctx, sel, v) +} + func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NamespaceData) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -14170,38 +14474,6 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S return res } -func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) { - var vSlice []interface{} - if v != nil { - vSlice = graphql.CoerceList(v) - } - var err error - res := make([]string, len(vSlice)) - for i := range vSlice { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) - res[i], err = ec.unmarshalNString2string(ctx, vSlice[i]) - if err != nil { - return nil, err - } - } - return res, nil -} - -func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - for i := range v { - ret[i] = ec.marshalNString2string(ctx, sel, v[i]) - } - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - func (ec *executionContext) marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivyImage) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index 397b571e..1aff036e 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -2,6 +2,10 @@ package model +type Cluster struct { + Name string `json:"name"` +} + type ClusterAPIsCount struct { ClusterName string `json:"clusterName"` Count int `json:"count"` @@ -105,6 +109,10 @@ type Kubescore struct { ExpiryDate *string `json:"expiryDate,omitempty"` } +type Namespace struct { + Name string `json:"name"` +} + type NamespaceData struct { Namespace string `json:"namespace"` OutdatedImages []*OutdatedImage `json:"outdatedImages"` diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index 72ef18ef..37783b48 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -10,8 +10,8 @@ type Query { allKubeScores: [Kubescore!]! allTrivyVuls: [TrivyVul!]! allTrivyMisconfigs: [TrivyMisconfig!]! - uniqueClusters: [String!]! - uniqueNamespaces: [String!]! + uniqueNamespaces: [Namespace!]! + uniqueClusters: [Cluster!]! outdatedImagesByClusterAndNamespace(clusterName: String!, namespace: String!): [OutdatedImage!]! outdatedImagesCount(clusterName: String!, namespace: String!): Int! allClusterNamespaceOutdatedCounts: [ClusterNamespaceOutdatedCount!]! @@ -19,6 +19,15 @@ type Query { allClusterDeletedAPIsCounts: [ClusterAPIsCount!]! allClusterNamespaceResourceCounts: [ClusterNamespaceResourceCount!]! } + +type Namespace { + name: String! +} + +type Cluster { + name: String! +} + type ClusterNamespaceResourceCount { clusterName: String! namespace: String! diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index 296b70e4..e6229bc9 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -395,38 +395,34 @@ func (r *queryResolver) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyM return misconfigs, nil } -// UniqueClusters is the resolver for the uniqueClusters field. -func (r *queryResolver) UniqueClusters(ctx context.Context) ([]string, error) { - if r.DB == nil { - return nil, fmt.Errorf("database connection is not initialized") - } - query := `SELECT DISTINCT ClusterName FROM events` - - rows, err := r.DB.QueryContext(ctx, query) +// UniqueNamespaces is the resolver for the uniqueNamespaces field. +func (r *queryResolver) UniqueNamespaces(ctx context.Context) ([]*model.Namespace, error) { + namespaces, err := r.fetchNamespacesFromDatabase(ctx) if err != nil { - return nil, fmt.Errorf("error executing query: %v", err) + return nil, err } - defer rows.Close() - var clusters []string - for rows.Next() { - var cluster string - if err := rows.Scan(&cluster); err != nil { - return nil, fmt.Errorf("error scanning row: %v", err) - } - clusters = append(clusters, cluster) + var namespaceObjects []*model.Namespace + for _, ns := range namespaces { + namespaceObjects = append(namespaceObjects, &model.Namespace{Name: ns}) } - if err := rows.Err(); err != nil { - return nil, fmt.Errorf("error iterating rows: %v", err) + return namespaceObjects, nil +} + +// UniqueClusters is the resolver for the uniqueClusters field. +func (r *queryResolver) UniqueClusters(ctx context.Context) ([]*model.Cluster, error) { + clusters, err := r.fetchClustersFromDatabase(ctx) + if err != nil { + return nil, err } - return clusters, nil -} + var clusterObjects []*model.Cluster + for _, cluster := range clusters { + clusterObjects = append(clusterObjects, &model.Cluster{Name: cluster}) + } -// UniqueNamespaces is the resolver for the uniqueNamespaces field. -func (r *queryResolver) UniqueNamespaces(ctx context.Context) ([]string, error) { - return r.fetchNamespacesFromDatabase(ctx) + return clusterObjects, nil } // OutdatedImagesByClusterAndNamespace is the resolver for the outdatedImagesByClusterAndNamespace field. diff --git a/graphqlserver/graph/utils.go b/graphqlserver/graph/utils.go index 23959ee4..01ad5c2d 100644 --- a/graphqlserver/graph/utils.go +++ b/graphqlserver/graph/utils.go @@ -8,6 +8,34 @@ import ( "github.com/intelops/kubviz/graphqlserver/graph/model" ) +func (r *Resolver) fetchClustersFromDatabase(ctx context.Context) ([]string, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + query := `SELECT DISTINCT ClusterName FROM events` + + rows, err := r.DB.QueryContext(ctx, query) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var clusters []string + for rows.Next() { + var cluster string + if err := rows.Scan(&cluster); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + clusters = append(clusters, cluster) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return clusters, nil +} + func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, error) { if r.DB == nil { return nil, fmt.Errorf("database connection is not initialized") From 75bfbf0009f7fafe085588b48ecce894c02c426d Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 14 Feb 2024 14:58:41 +0530 Subject: [PATCH 202/263] using kuberhealthy package struct for parsing payload --- .../plugins/kuberhealthy/kuberhealthy.go | 46 +- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 12 +- charts/agent/values.yaml | 17 +- go.mod | 1 + go.sum | 474 ++++++++++++++++++ 6 files changed, 507 insertions(+), 45 deletions(-) diff --git a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go index 7714ba35..2ae66ccd 100644 --- a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go +++ b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go @@ -7,13 +7,12 @@ import ( "io" "log" "net/http" - "strings" "time" "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/constants" - "github.com/intelops/kubviz/model" "github.com/intelops/kubviz/pkg/opentelemetry" + "github.com/kuberhealthy/kuberhealthy/v2/pkg/health" "github.com/nats-io/nats.go" "go.opentelemetry.io/otel" ) @@ -45,52 +44,29 @@ func pollAndPublishKuberhealthy(url string, js nats.JetStreamContext) error { return fmt.Errorf("error reading response body: %w", err) } - var state model.State + var state health.State if err := json.Unmarshal(body, &state); err != nil { return fmt.Errorf("error unmarshaling response: %w", err) } return PublishKuberhealthyMetrics(js, state) } -func boolToUInt8(b bool) uint8 { - if b { - return 1 - } - return 0 -} -func errorsToString(errors []string) string { - return strings.Join(errors, ", ") -} -func PublishKuberhealthyMetrics(js nats.JetStreamContext, state model.State) error { +func PublishKuberhealthyMetrics(js nats.JetStreamContext, state health.State) error { ctx := context.Background() tracer := otel.Tracer("kuberhealthy") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishKuberhealthyMetrics") defer span.End() - for checkName, details := range state.CheckDetails { - metrics := model.KuberhealthyCheckDetail{ - CurrentUUID: details.CurrentUUID, - CheckName: checkName, - OK: boolToUInt8(details.OK), - Errors: errorsToString(details.Errors), - RunDuration: details.RunDuration, - Namespace: details.Namespace, - Node: details.Node, - LastRun: details.LastRun.Time, - AuthoritativePod: details.AuthoritativePod, - } - - metricsJSON, err := json.Marshal(metrics) - if err != nil { - log.Printf("Error marshaling metrics of kuberhealthy %s: %v", checkName, err) - continue - } + metricsJSON, err := json.Marshal(state) + if err != nil { + log.Printf("Error marshaling metrics of kuberhealthy %v", err) + return err + } - if _, err := js.Publish(constants.KUBERHEALTHY_SUBJECT, metricsJSON); err != nil { - log.Printf("Error publishing metrics for kuberhealthy %s: %v", checkName, err) - continue - } + if _, err := js.Publish(constants.KUBERHEALTHY_SUBJECT, metricsJSON); err != nil { + log.Printf("Error publishing metrics for kuberhealthy %v", err) + return err } log.Printf("Kuberhealthy metrics have been published") diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 57e0526b..35de376e 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.12 +version: 1.1.13 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index acaa05a5..5321f029 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -46,6 +46,10 @@ spec: # path: / # port: http env: + - name: KUBERHEALTHY_URL + value: {{ .Values.kuberhealthy.url }} + - name: POLL_INTERVAL + value: {{ .Values.kuberhealthy.pollInterval }} - name: CLUSTER_NAME value: {{ .Values.clusterName }} - name: NATS_TOKEN @@ -57,6 +61,8 @@ spec: name: {{ .Values.nats.auth.secret.name }} key: {{ .Values.nats.auth.secret.key }} {{- end }} + - name: KUBERHEALTHY_ENABLE + value: {{ .Values.kuberhealthy.enabled }} - name: NATS_ADDRESS value: {{ .Values.nats.host }} - name: SCHEDULING_INTERVAL @@ -81,7 +87,7 @@ spec: value: {{ .Values.opentelemetry.url }} - name : APPLICATION_NAME value : {{ .Values.opentelemetry.appName }} - {{- if .Values.persistence.enabled }} + {{- if .Values.persistence.enabled }} volumeMounts: - name: data mountPath: {{ .Values.persistence.mountPath }} @@ -127,7 +133,7 @@ spec: value: {{ .Values.opentelemetry.url }} - name : APPLICATION_NAME value : {{ .Values.opentelemetry.appName }} - {{- if .Values.git_bridge.persistence.enabled }} + {{- if .Values.git_bridge.persistence.enabled }} volumeMounts: - name: data mountPath: {{ .Values.git_bridge.persistence.mountPath }} @@ -174,7 +180,7 @@ spec: value: {{ .Values.opentelemetry.url }} - name : APPLICATION_NAME value : {{ .Values.opentelemetry.appName }} - {{- if .Values.container_bridge.persistence.enabled }} + {{- if .Values.container_bridge.persistence.enabled }} volumeMounts: - name: data mountPath: {{ .Values.container_bridge.persistence.mountPath }} diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index f5b7b304..f119e92a 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -28,7 +28,7 @@ serviceAccount: podAnnotations: {} podSecurityContext: - fsGroup: 1001 + fsGroup: 1001 runAsUser: 1001 runAsGroup: 1001 @@ -52,7 +52,7 @@ git_bridge: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always tag: "v1.1.4" - resources: + resources: limits: cpu: 200m memory: 256Mi @@ -96,7 +96,7 @@ container_bridge: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always tag: "v1.1.4" - resources: + resources: limits: cpu: 200m memory: 256Mi @@ -131,7 +131,7 @@ container_bridge: # hosts: # - chart-example.local - + ingress: enabled: false annotations: {} @@ -162,7 +162,7 @@ persistence: mountPath: /mnt/agent/kbz accessMode: ReadWriteOnce size: 5Gi - + autoscaling: enabled: false minReplicas: 1 @@ -186,6 +186,11 @@ schedule: kubepreupgradeInterval: "@every 22h" trivyInterval: "@every 24h" +kuberhealthy: + enabled: true + pollInterval: "15m" + url: "http://localhost:8080" + opentelemetry: isEnabled: false url: "otelcollector.local" @@ -197,7 +202,7 @@ externalSecrets: create: false nats: - host: kubviz-client-nats + host: kubviz-client-nats auth: # Use token if you want to provide the token via Helm Values token: "" diff --git a/go.mod b/go.mod index 41cc1277..fa52bd8c 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/hashicorp/go-version v1.6.0 github.com/intelops/go-common v1.0.19 github.com/kelseyhightower/envconfig v1.4.0 + github.com/kuberhealthy/kuberhealthy/v2 v2.7.1 github.com/nats-io/nats.go v1.27.1 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 diff --git a/go.sum b/go.sum index 1098ebc7..9aaeb1a6 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,64 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/99designs/gqlgen v0.17.42 h1:BVWDOb2VVHQC5k3m6oa0XhDnxltLLrU4so7x/u39Zu4= github.com/99designs/gqlgen v0.17.42/go.mod h1:GQ6SyMhwFbgHR0a8r2Wn8fYgEwPxxmndLFPhU63+cJE= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.52.1 h1:nucdgfD1BDSHjbNaG3VNebonxJzD8fX8jbuBpfo5VY0= github.com/ClickHouse/ch-go v0.52.1/go.mod h1:B9htMJ0hii/zrC2hljUKdnagRBuLqtRG/GrU3jqCwRk= github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= @@ -30,9 +75,13 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8 github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/Pallinder/go-randomdata v1.1.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= @@ -62,7 +111,9 @@ github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230628140707-dae3bdb6ee81 h1 github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230628140707-dae3bdb6ee81/go.mod h1:GCm7uq++jz7Ij8cA9mAorpKJ9/qSBCl7v6EKYA8DxJ8= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/aws/aws-sdk-go v1.25.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.245 h1:KtY2s4q31/kn33AdV63R5t77mdxsI7rq3YT7Mgo805M= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -77,6 +128,7 @@ github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4 github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -91,6 +143,10 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/codingsince1985/checksum v1.1.0/go.mod h1:oOS5kmF4DPKaeMAT7CCXcvakSB7s08Om5FdZTwV/CEk= github.com/containerd/containerd v1.2.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -105,6 +161,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/denverdino/aliyungo v0.0.0-20191023002520-dba750c0c223/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw= @@ -128,17 +185,28 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/genuinetools/pkg v0.0.0-20181022210355-2fcf164d37cb/go.mod h1:XTcrCYlXPxnxL2UpnwuRn7tcaTn9HAhxFoFJucootk8= @@ -163,18 +231,30 @@ github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gorp/gorp/v3 v3.0.5 h1:PUjzYdYu3HBOh8LE+UUmRG2P0IRDak9XMeGNvaeq4Ow= +github.com/go-ini/ini v1.49.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= @@ -188,6 +268,7 @@ github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QX github.com/go-playground/webhooks/v6 v6.2.0 h1:SV/Euz3xoTc7LQanUtXaYhVQU0rw4DaxNhNKOBZ90JI= github.com/go-playground/webhooks/v6 v6.2.0/go.mod h1:GCocmfMtpJdkEOM1uG9p2nXzg1kY5X/LtvQgtPHUaaA= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -202,22 +283,40 @@ github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8 github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= @@ -226,29 +325,60 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/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.5/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.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= @@ -268,26 +398,41 @@ github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhE github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20190402204710-8ff2fc3824fc/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/integrii/flaggy v1.2.2/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI= github.com/intelops/go-common v1.0.19 h1:K53TIISpeTONS4g1squwGvo+1wmSbv2Kqp31mpw1090= github.com/intelops/go-common v1.0.19/go.mod h1:GDDr2xP2uqtjMgATC4BLDt29kC7W9R3EW+8Du2LlNt8= github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= 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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -303,15 +448,19 @@ github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 h1:aC6MEAs3PE3lWD7lqrJfDxHd6hcced9R4JTZu85cJwU= github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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/kuberhealthy/kuberhealthy/v2 v2.7.1 h1:N7kVL1HbO2zEu+zkcMa5oXQDhij9De38ZYL+p959bgk= +github.com/kuberhealthy/kuberhealthy/v2 v2.7.1/go.mod h1:NRbBdxZLgIf+cy8PUau9q74nOqPLeC9HWr5faYKd9G8= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= @@ -339,20 +488,25 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 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/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/moby/buildkit v0.11.6 h1:VYNdoKk5TVxN7k4RvZgdeM4GOyRvIi4Z8MXOY7xvyUs= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= @@ -362,8 +516,10 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4= github.com/nats-io/nats-server/v2 v2.9.19 h1:OF9jSKZGo425C/FcVVIvNgpd36CUe7aVTTXEZRJk6kA= github.com/nats-io/nats-server/v2 v2.9.19/go.mod h1:aTb/xtLCGKhfTFLxP591CMWfkdgBmcUUSkiSOe5A3gw= @@ -373,9 +529,22 @@ github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/open-policy-agent/opa v0.45.0 h1:P5nuhVRtR+e58fk3CMMbiqr6ZFyWQPNOC3otsorGsFs= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -407,6 +576,7 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= @@ -420,6 +590,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -439,27 +610,34 @@ github.com/showa-93/go-mask v0.6.0/go.mod h1:aswIj007gm0EPAzOGES9ACy1jDm3QT08/LP github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4= github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/spdx/tools-golang v0.5.0 h1:/fqihV2Jna7fmow65dHpgKNsilgLK7ICpd2tkCnPEyY= github.com/spdx/tools-golang v0.5.0/go.mod h1:kkGlrSXXfHwuSzHQZJRV3aKu9ZXCq/MSf2+xyiJH1lM= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -493,8 +671,11 @@ github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0= github.com/zegl/kube-score v1.17.0 h1:vedzK0pm5yOb1ocm5gybMNYsJRG8iTAatbo3LFIWbUc= @@ -502,6 +683,13 @@ github.com/zegl/kube-score v1.17.0/go.mod h1:0pt4Lt36uTKPiCQbXQFow29eaAbgMLI9RoE go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 h1:mMv2jG58h6ZI5t5S9QCVGdzCmAsTakMa3oxVgpSD44g= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1/go.mod h1:oqRuNKG0upTaDPbLVCG8AD0G2ETrfDtmh7jViy7ox6M= @@ -533,55 +721,179 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.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.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -589,17 +901,26 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -607,10 +928,54 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= 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= @@ -618,25 +983,104 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb h1:Isk1sSH7bovx8Rti2wZK0UZF6oraBDK74uoyLEEVFN0= google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -645,8 +1089,10 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -655,21 +1101,29 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/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.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/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= @@ -678,34 +1132,54 @@ gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= helm.sh/helm/v3 v3.12.1 h1:lzU7etZX24A6BTMXYQF3bFq0ECfD8s+fKlNBBL8AbEc= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg= k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= +k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/apiserver v0.27.2 h1:p+tjwrcQEZDrEorCZV2/qE8osGTINPuS5ZNqWAvKm5E= k8s.io/cli-runtime v0.27.3 h1:h592I+2eJfXj/4jVYM+tu9Rv8FEc/dyCoD80UJlMW2Y= k8s.io/cli-runtime v0.27.3/go.mod h1:LzXud3vFFuDFXn2LIrWnscPgUiEj7gQQcYZE2UPn9Kw= +k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU= k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kops v1.11.0/go.mod h1:Rj0HgVofTwl4lTemGYjf2uUNLoNsEZMhdmt8jG74xLI= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/kubectl v0.27.2 h1:sSBM2j94MHBFRWfHIWtEXWCicViQzZsb177rNsKBhZg= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= From 67a645864d30b11a45485f23826c536befb4f626 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 14 Feb 2024 15:09:06 +0530 Subject: [PATCH 203/263] minor changes --- model/kuberhealthy.go | 44 ++++++++------------------------- sql/0000021_kuberhealthy.up.sql | 2 +- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/model/kuberhealthy.go b/model/kuberhealthy.go index 76c84d0e..c613cece 100644 --- a/model/kuberhealthy.go +++ b/model/kuberhealthy.go @@ -1,39 +1,15 @@ package model -import ( - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type State struct { - OK bool `json:"OK"` - Errors []string `json:"Errors"` - CheckDetails map[string]WorkloadDetails `json:"CheckDetails"` // map of check names to last run timestamp - JobDetails map[string]WorkloadDetails `json:"JobDetails"` // map of job names to last run timestamp - CurrentMaster string `json:"CurrentMaster"` - Metadata map[string]string `json:"Metadata"` -} - -type WorkloadDetails struct { - OK bool `json:"OK" yaml:"OK"` // true or false status of the khWorkload, whether or not it completed successfully - Errors []string `json:"Errors" yaml:"Errors"` // the list of errors reported from the khWorkload run - RunDuration string `json:"RunDuration" yaml:"RunDuration"` // the time it took for the khWorkload to complete - Namespace string `json:"Namespace" yaml:"Namespace"` // the namespace the khWorkload was run in - Node string `json:"Node" yaml:"Node"` // the node the khWorkload ran on - LastRun *metav1.Time `json:"LastRun,omitempty" yaml:"LastRun,omitempty"` // the time the khWorkload was last run - AuthoritativePod string `json:"AuthoritativePod" yaml:"AuthoritativePod"` // the main kuberhealthy pod creating and updating the khstate - CurrentUUID string `json:"uuid" yaml:"uuid"` // the UUID that is authorized to report statuses into the kuberhealthy endpoint -} +// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type KuberhealthyCheckDetail struct { - CurrentUUID string `json:"currentUUID"` - CheckName string `json:"checkName"` - OK uint8 `json:"ok"` - Errors string `json:"errors"` - RunDuration string `json:"runDuration"` - Namespace string `json:"namespace"` - Node string `json:"node"` - LastRun time.Time `json:"lastRun"` - AuthoritativePod string `json:"authoritativePod"` + CurrentUUID string `json:"currentUUID"` + CheckName string `json:"checkName"` + OK uint8 `json:"ok"` + Errors string `json:"errors"` + RunDuration string `json:"runDuration"` + Namespace string `json:"namespace"` + Node string `json:"node"` + LastRun string `json:"lastRun"` + AuthoritativePod string `json:"authoritativePod"` } diff --git a/sql/0000021_kuberhealthy.up.sql b/sql/0000021_kuberhealthy.up.sql index 62ce6e51..270cc8cf 100644 --- a/sql/0000021_kuberhealthy.up.sql +++ b/sql/0000021_kuberhealthy.up.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS kuberhealthy ( RunDuration String, Namespace String, Node String, - LastRun DateTime('UTC'), + LastRun String, AuthoritativePod String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() From ff91e51997d7caa6387b0041915422d3bab2ea05 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 14 Feb 2024 15:23:03 +0530 Subject: [PATCH 204/263] minor change --- model/kuberhealthy.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/model/kuberhealthy.go b/model/kuberhealthy.go index c613cece..7ce2f931 100644 --- a/model/kuberhealthy.go +++ b/model/kuberhealthy.go @@ -1,15 +1,17 @@ package model +import "time" + // metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type KuberhealthyCheckDetail struct { - CurrentUUID string `json:"currentUUID"` - CheckName string `json:"checkName"` - OK uint8 `json:"ok"` - Errors string `json:"errors"` - RunDuration string `json:"runDuration"` - Namespace string `json:"namespace"` - Node string `json:"node"` - LastRun string `json:"lastRun"` - AuthoritativePod string `json:"authoritativePod"` + CurrentUUID string `json:"currentUUID"` + CheckName string `json:"checkName"` + OK uint8 `json:"ok"` + Errors string `json:"errors"` + RunDuration string `json:"runDuration"` + Namespace string `json:"namespace"` + Node string `json:"node"` + LastRun time.Time `json:"lastRun"` + AuthoritativePod string `json:"authoritativePod"` } From 185eefc706721bc3f5efc7a6728fb235b7a7caaf Mon Sep 17 00:00:00 2001 From: vijeyash Date: Wed, 14 Feb 2024 15:24:57 +0530 Subject: [PATCH 205/263] minor change --- sql/0000021_kuberhealthy.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/0000021_kuberhealthy.up.sql b/sql/0000021_kuberhealthy.up.sql index 270cc8cf..62ce6e51 100644 --- a/sql/0000021_kuberhealthy.up.sql +++ b/sql/0000021_kuberhealthy.up.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS kuberhealthy ( RunDuration String, Namespace String, Node String, - LastRun String, + LastRun DateTime('UTC'), AuthoritativePod String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} ) ENGINE = MergeTree() From 324348dcf0067ec41c517c9e4c35b083fad98f08 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 14 Feb 2024 22:06:39 +0530 Subject: [PATCH 206/263] kuberhealthy-client --- client/pkg/clickhouse/db_client.go | 72 ++++++++++++++++++++++------- client/pkg/clickhouse/statements.go | 1 + client/pkg/clients/kubviz_client.go | 16 +++++++ client/pkg/config/config.go | 1 + 4 files changed, 73 insertions(+), 17 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index b0e279e9..315d0c35 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -27,6 +27,7 @@ type DBClient struct { conf *config.Config } type DBInterface interface { + InsertKuberhealthyMetrics(model.KuberhealthyCheckDetail) InsertRakeesMetrics(model.RakeesMetrics) InsertKetallEvent(model.Resource) InsertOutdatedEvent(model.CheckResultfinal) @@ -137,7 +138,7 @@ func NewDBClient(conf *config.Config) (DBInterface, *sql.DB, error) { func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushEventPayload) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-container-azure") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventAzure") span.SetAttributes(attribute.String("container-azure-client", "insert")) @@ -193,7 +194,7 @@ func (c *DBClient) InsertContainerEventAzure(pushEvent model.AzureContainerPushE func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-container-quay") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventQuay") span.SetAttributes(attribute.String("container-quay-client", "insert")) @@ -251,7 +252,7 @@ func (c *DBClient) InsertContainerEventQuay(pushEvent model.QuayImagePushPayload func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushEventPayload) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-container-jfrog") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertContainerEventJfrog") span.SetAttributes(attribute.String("container-jfrog-client", "insert")) @@ -309,7 +310,7 @@ func (c *DBClient) InsertContainerEventJfrog(pushEvent model.JfrogContainerPushE func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-rakees-metrics") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertRakeesMetrics") span.SetAttributes(attribute.String("rakees-client", "insert")) @@ -346,7 +347,7 @@ func (c *DBClient) InsertRakeesMetrics(metrics model.RakeesMetrics) { func (c *DBClient) InsertKetallEvent(metrics model.Resource) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-ketall-event") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKetallEvent") span.SetAttributes(attribute.String("ketall-client", "insert")) @@ -380,9 +381,46 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { } } +func (c *DBClient) InsertKuberhealthyMetrics(metrics model.KuberhealthyCheckDetail) { + ctx := context.Background() + tracer := otel.Tracer("insert-kuberhealthy") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKuberhealthy") + span.SetAttributes(attribute.String("kuberhealthy-client", "insert")) + defer span.End() + log.Println("****metrics", metrics) + + tx, err := c.conn.Begin() + if err != nil { + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) + } + + stmt, err := tx.Prepare(InsertKuberhealthy) + if err != nil { + log.Fatalf("error preparing statement: %v", err) + } + + if _, err := stmt.Exec( + metrics.CurrentUUID, + metrics.CheckName, + metrics.OK, + metrics.Errors, + metrics.RunDuration, + metrics.Namespace, + metrics.Node, + metrics.LastRun, + metrics.AuthoritativePod, + ); err != nil { + log.Fatal(err) + } + if err := tx.Commit(); err != nil { + log.Fatal(err) + } + stmt.Close() +} + func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-outdated-event") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertOutdatedEvent") span.SetAttributes(attribute.String("outdated-client", "insert")) @@ -420,7 +458,7 @@ func (c *DBClient) InsertOutdatedEvent(metrics model.CheckResultfinal) { func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-depricated-event") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertDeprecatedAPI") span.SetAttributes(attribute.String("depricated-client", "insert")) @@ -465,7 +503,7 @@ func (c *DBClient) InsertDeprecatedAPI(deprecatedAPI model.DeprecatedAPI) { func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-deletedapi") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertDeletedAPI") span.SetAttributes(attribute.String("deletedapi-client", "insert")) @@ -511,7 +549,7 @@ func (c *DBClient) InsertDeletedAPI(deletedAPI model.DeletedAPI) { func (c *DBClient) InsertKubvizEvent(metrics model.Metrics) { - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("insert-kubviz-event") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKubvizEvent") span.SetAttributes(attribute.String("kubvizevent-client", "insert")) @@ -792,14 +830,14 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { span.SetAttributes(attribute.String("trivy-sbom-client", "insert")) defer span.End() - tx, err := c.conn.Begin() - if err != nil { - log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) - } - stmt, err := tx.Prepare(InsertTrivySbom) - if err != nil { - log.Fatalf("error preparing statement: %v", err) - } + tx, err := c.conn.Begin() + if err != nil { + log.Fatalf("error beginning transaction, clickhouse connection not available: %v", err) + } + stmt, err := tx.Prepare(InsertTrivySbom) + if err != nil { + log.Fatalf("error preparing statement: %v", err) + } if _, err := stmt.Exec( metrics.ID, diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 19e0c377..a728ceec 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -233,3 +233,4 @@ const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpus const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertKuberhealthy string = "INSERT INTO kuberhealthy (CurrentUUID, CheckName, OK, Errors, RunDuration, Namespace, Node, LastRun, AuthoritativePod) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" \ No newline at end of file diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index 3f984f98..ece102da 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -69,6 +69,22 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, + { + Subject: constants.KUBERHEALTHY_SUBJECT, + Consumer: cfg.KuberhealthyConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.KuberhealthyCheckDetail + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Println("failed to unmarshal from nats", err) + return + } + log.Printf("Kuberhealthy Metrics Received: %#v,", metrics) + conn.InsertKuberhealthyMetrics(metrics) + log.Println() + }, + }, { Subject: constants.OutdatedSubject, Consumer: cfg.OutdatedConsumer, diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index 7f030aa9..7fb49a2c 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -17,4 +17,5 @@ type Config struct { TrivyConsumer string `envconfig:"TRIVY_CONSUMER" required:"true"` TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER" required:"true"` TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER" required:"true"` + KuberhealthyConsumer string `envconfig:"KUBERHEALTHY_CONSUMER" required:"true"` } From eaaff47e0fc1377f6047318dce2b79ea6e673d2c Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Wed, 14 Feb 2024 22:58:32 +0530 Subject: [PATCH 207/263] issuefix1 --- client/pkg/clickhouse/db_client.go | 31 ++++++++++++++++------------- client/pkg/clients/kubviz_client.go | 3 ++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 315d0c35..60c50482 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ClickHouse/clickhouse-go/v2" + "github.com/kuberhealthy/kuberhealthy/v2/pkg/health" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -27,7 +28,7 @@ type DBClient struct { conf *config.Config } type DBInterface interface { - InsertKuberhealthyMetrics(model.KuberhealthyCheckDetail) + InsertKuberhealthyMetrics(health.State) InsertRakeesMetrics(model.RakeesMetrics) InsertKetallEvent(model.Resource) InsertOutdatedEvent(model.CheckResultfinal) @@ -381,7 +382,7 @@ func (c *DBClient) InsertKetallEvent(metrics model.Resource) { } } -func (c *DBClient) InsertKuberhealthyMetrics(metrics model.KuberhealthyCheckDetail) { +func (c *DBClient) InsertKuberhealthyMetrics(metrics health.State) { ctx := context.Background() tracer := otel.Tracer("insert-kuberhealthy") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKuberhealthy") @@ -399,18 +400,20 @@ func (c *DBClient) InsertKuberhealthyMetrics(metrics model.KuberhealthyCheckDeta log.Fatalf("error preparing statement: %v", err) } - if _, err := stmt.Exec( - metrics.CurrentUUID, - metrics.CheckName, - metrics.OK, - metrics.Errors, - metrics.RunDuration, - metrics.Namespace, - metrics.Node, - metrics.LastRun, - metrics.AuthoritativePod, - ); err != nil { - log.Fatal(err) + for _, checkdata := range metrics.CheckDetails { + if _, err := stmt.Exec( + checkdata.CurrentUUID, + checkdata.CurrentUUID, + checkdata.OK, + checkdata.Errors, + checkdata.RunDuration, + checkdata.Namespace, + checkdata.Node, + checkdata.LastRun, + checkdata.AuthoritativePod, + ); err != nil { + log.Fatal(err) + } } if err := tx.Commit(); err != nil { log.Fatal(err) diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index ece102da..b126aaa7 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -8,6 +8,7 @@ import ( "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kelseyhightower/envconfig" + "github.com/kuberhealthy/kuberhealthy/v2/pkg/health" "github.com/nats-io/nats.go" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -74,7 +75,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { Consumer: cfg.KuberhealthyConsumer, Handler: func(msg *nats.Msg) { msg.Ack() - var metrics model.KuberhealthyCheckDetail + var metrics health.State err := json.Unmarshal(msg.Data, &metrics) if err != nil { log.Println("failed to unmarshal from nats", err) From be6c3fc649e75bd7d00da3cf0ea7f2a7e17daaea Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 00:59:53 +0530 Subject: [PATCH 208/263] log --- agent/kubviz/plugins/kuberhealthy/kuberhealthy.go | 1 + 1 file changed, 1 insertion(+) diff --git a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go index 2ae66ccd..d9f4ac55 100644 --- a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go +++ b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go @@ -68,6 +68,7 @@ func PublishKuberhealthyMetrics(js nats.JetStreamContext, state health.State) er log.Printf("Error publishing metrics for kuberhealthy %v", err) return err } + log.Println("%%%%after publish", string(metricsJSON)) log.Printf("Kuberhealthy metrics have been published") return nil From 322a8074823e0309e41b6f76528882700f7ef7ab Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 01:21:13 +0530 Subject: [PATCH 209/263] log2 --- agent/kubviz/k8smetrics_agent.go | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 76ae1926..b706915f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -20,14 +20,14 @@ import ( "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/agent/kubviz/plugins/events" - "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + //"github.com/intelops/kubviz/agent/kubviz/plugins/ketall" + //"github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" - "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" - "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" + //"github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + //"github.com/intelops/kubviz/agent/kubviz/plugins/trivy" "github.com/intelops/kubviz/agent/kubviz/scheduler" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" @@ -135,20 +135,20 @@ func main() { collectAndPublishMetrics := func() { err := outdated.OutDatedImages(config, js) events.LogErr(err) - err = kubepreupgrade.KubePreUpgradeDetector(config, js) - events.LogErr(err) - err = ketall.GetAllResources(config, js) - events.LogErr(err) - err = rakkess.RakeesOutput(config, js) - events.LogErr(err) - err = trivy.RunTrivySbomScan(config, js) - events.LogErr(err) - err = trivy.RunTrivyImageScans(config, js) - events.LogErr(err) - err = trivy.RunTrivyK8sClusterScan(js) - events.LogErr(err) - err = kubescore.RunKubeScore(clientset, js) - events.LogErr(err) + // err = kubepreupgrade.KubePreUpgradeDetector(config, js) + // events.LogErr(err) + // err = ketall.GetAllResources(config, js) + // events.LogErr(err) + // err = rakkess.RakeesOutput(config, js) + // events.LogErr(err) + // err = trivy.RunTrivySbomScan(config, js) + // events.LogErr(err) + // err = trivy.RunTrivyImageScans(config, js) + // events.LogErr(err) + // err = trivy.RunTrivyK8sClusterScan(js) + // events.LogErr(err) + // err = kubescore.RunKubeScore(clientset, js) + // events.LogErr(err) } collectAndPublishMetrics() From 7ae5869bcef28ffb436f4e85a3aadaefd8fd24ca Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 01:54:57 +0530 Subject: [PATCH 210/263] log3 --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index b706915f..62e99f8c 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -63,7 +63,7 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - clusterMetricsChan := make(chan error, 1) + //clusterMetricsChan := make(chan error, 1) cfg, err := config.GetAgentConfigurations() if err != nil { log.Fatal("Failed to retrieve agent configurations", err) @@ -127,7 +127,7 @@ func main() { } }() - go events.PublishMetrics(clientset, js, clusterMetricsChan) + //go events.PublishMetrics(clientset, js, clusterMetricsChan) if cfg.KuberHealthyEnable { go kuberhealthy.StartKuberHealthy(js) } From b5a8dffe3d64b00593cf3d2faaa493d031d63399 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 11:28:17 +0530 Subject: [PATCH 211/263] client-test --- agent/kubviz/plugins/kuberhealthy/kuberhealthy.go | 1 - pkg/opentelemetry/opentelemetry.go | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go index d9f4ac55..2ae66ccd 100644 --- a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go +++ b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go @@ -68,7 +68,6 @@ func PublishKuberhealthyMetrics(js nats.JetStreamContext, state health.State) er log.Printf("Error publishing metrics for kuberhealthy %v", err) return err } - log.Println("%%%%after publish", string(metricsJSON)) log.Printf("Kuberhealthy metrics have been published") return nil diff --git a/pkg/opentelemetry/opentelemetry.go b/pkg/opentelemetry/opentelemetry.go index f3c6d2dd..1346bae2 100644 --- a/pkg/opentelemetry/opentelemetry.go +++ b/pkg/opentelemetry/opentelemetry.go @@ -19,6 +19,7 @@ import ( type Configurations struct { ServiceName string `envconfig:"APPLICATION_NAME" default:"Kubviz"` CollectorURL string `envconfig:"OPTEL_URL" default:"otelcollector.azureagent.optimizor.app:80"` + IsEnabled bool `envconfig:"IS_OPTEL_ENABLED" default:"false"` } func GetConfigurations() (opteConfig *Configurations, err error) { @@ -38,6 +39,10 @@ func InitTracer() (*sdktrace.TracerProvider, error) { return nil, err } + if !config.IsEnabled { + return nil, nil + } + headers := map[string]string{ "signoz-service-name": config.ServiceName, } From 562c7ecbfd554d0f862cc95404ac9fd19e8edd18 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 11:43:07 +0530 Subject: [PATCH 212/263] debugging --- agent/kubviz/k8smetrics_agent.go | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 62e99f8c..76ae1926 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -20,14 +20,14 @@ import ( "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/agent/kubviz/plugins/events" - //"github.com/intelops/kubviz/agent/kubviz/plugins/ketall" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" - //"github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" - //"github.com/intelops/kubviz/agent/kubviz/plugins/trivy" + "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" "github.com/intelops/kubviz/agent/kubviz/scheduler" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" @@ -63,7 +63,7 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - //clusterMetricsChan := make(chan error, 1) + clusterMetricsChan := make(chan error, 1) cfg, err := config.GetAgentConfigurations() if err != nil { log.Fatal("Failed to retrieve agent configurations", err) @@ -127,7 +127,7 @@ func main() { } }() - //go events.PublishMetrics(clientset, js, clusterMetricsChan) + go events.PublishMetrics(clientset, js, clusterMetricsChan) if cfg.KuberHealthyEnable { go kuberhealthy.StartKuberHealthy(js) } @@ -135,20 +135,20 @@ func main() { collectAndPublishMetrics := func() { err := outdated.OutDatedImages(config, js) events.LogErr(err) - // err = kubepreupgrade.KubePreUpgradeDetector(config, js) - // events.LogErr(err) - // err = ketall.GetAllResources(config, js) - // events.LogErr(err) - // err = rakkess.RakeesOutput(config, js) - // events.LogErr(err) - // err = trivy.RunTrivySbomScan(config, js) - // events.LogErr(err) - // err = trivy.RunTrivyImageScans(config, js) - // events.LogErr(err) - // err = trivy.RunTrivyK8sClusterScan(js) - // events.LogErr(err) - // err = kubescore.RunKubeScore(clientset, js) - // events.LogErr(err) + err = kubepreupgrade.KubePreUpgradeDetector(config, js) + events.LogErr(err) + err = ketall.GetAllResources(config, js) + events.LogErr(err) + err = rakkess.RakeesOutput(config, js) + events.LogErr(err) + err = trivy.RunTrivySbomScan(config, js) + events.LogErr(err) + err = trivy.RunTrivyImageScans(config, js) + events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(js) + events.LogErr(err) + err = kubescore.RunKubeScore(clientset, js) + events.LogErr(err) } collectAndPublishMetrics() From 92dbdbce1292241ece5558bb39dad5728205702d Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 12:00:13 +0530 Subject: [PATCH 213/263] debugging --- agent/kubviz/k8smetrics_agent.go | 21 ++--- client/pkg/clients/kubviz_client.go | 122 ++++++++++++++-------------- 2 files changed, 72 insertions(+), 71 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 76ae1926..3f5fcf97 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -24,10 +24,11 @@ import ( "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" - "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" + + //"github.com/intelops/kubviz/agent/kubviz/plugins/trivy" "github.com/intelops/kubviz/agent/kubviz/scheduler" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" @@ -141,14 +142,14 @@ func main() { events.LogErr(err) err = rakkess.RakeesOutput(config, js) events.LogErr(err) - err = trivy.RunTrivySbomScan(config, js) - events.LogErr(err) - err = trivy.RunTrivyImageScans(config, js) - events.LogErr(err) - err = trivy.RunTrivyK8sClusterScan(js) - events.LogErr(err) - err = kubescore.RunKubeScore(clientset, js) - events.LogErr(err) + // err = trivy.RunTrivySbomScan(config, js) + // events.LogErr(err) + // err = trivy.RunTrivyImageScans(config, js) + // events.LogErr(err) + // err = trivy.RunTrivyK8sClusterScan(js) + // events.LogErr(err) + // err = kubescore.RunKubeScore(clientset, js) + // events.LogErr(err) } collectAndPublishMetrics() diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index b126aaa7..dcf12bff 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -131,37 +131,37 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, - { - Subject: constants.TRIVY_IMAGE_SUBJECT, - Consumer: cfg.TrivyImageConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.TrivyImage - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Trivy Metrics Received: %#v,", metrics) - conn.InsertTrivyImageMetrics(metrics) - log.Println() - }, - }, - { - Subject: constants.TRIVY_SBOM_SUBJECT, - Consumer: cfg.TrivySbomConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.SbomData - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Println("failed to unmarshal from nats", err) - return - } - log.Printf("Trivy sbom Metrics Received: %#v,", metrics) - conn.InsertTrivySbomMetrics(metrics) - log.Println() - }, - }, + // { + // Subject: constants.TRIVY_IMAGE_SUBJECT, + // Consumer: cfg.TrivyImageConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.TrivyImage + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Trivy Metrics Received: %#v,", metrics) + // conn.InsertTrivyImageMetrics(metrics) + // log.Println() + // }, + // }, + // { + // Subject: constants.TRIVY_SBOM_SUBJECT, + // Consumer: cfg.TrivySbomConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.SbomData + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Println("failed to unmarshal from nats", err) + // return + // } + // log.Printf("Trivy sbom Metrics Received: %#v,", metrics) + // conn.InsertTrivySbomMetrics(metrics) + // log.Println() + // }, + // }, { Subject: constants.KubvizSubject, Consumer: cfg.KubvizConsumer, @@ -177,36 +177,36 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, - { - Subject: constants.KUBESCORE_SUBJECT, - Consumer: cfg.KubscoreConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.KubeScoreRecommendations - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Kubscore Metrics Received: %#v,", metrics) - conn.InsertKubeScoreMetrics(metrics) - log.Println() - }, - }, - { - Subject: constants.TRIVY_K8S_SUBJECT, - Consumer: cfg.TrivyConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.Trivy - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Trivy Metrics Received: %#v,", metrics) - conn.InsertTrivyMetrics(metrics) - log.Println() - }, - }, + // { + // Subject: constants.KUBESCORE_SUBJECT, + // Consumer: cfg.KubscoreConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.KubeScoreRecommendations + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Kubscore Metrics Received: %#v,", metrics) + // conn.InsertKubeScoreMetrics(metrics) + // log.Println() + // }, + // }, + // { + // Subject: constants.TRIVY_K8S_SUBJECT, + // Consumer: cfg.TrivyConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.Trivy + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Trivy Metrics Received: %#v,", metrics) + // conn.InsertTrivyMetrics(metrics) + // log.Println() + // }, + // }, } for _, sub := range subscriptions { From 32a8bf44b3206533facca347255474cd7e6f495e Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 12:30:43 +0530 Subject: [PATCH 214/263] debugging --- agent/kubviz/k8smetrics_agent.go | 4 ++-- client/pkg/clients/kubviz_client.go | 30 ++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 3f5fcf97..47dd0571 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -64,7 +64,7 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - clusterMetricsChan := make(chan error, 1) + //clusterMetricsChan := make(chan error, 1) cfg, err := config.GetAgentConfigurations() if err != nil { log.Fatal("Failed to retrieve agent configurations", err) @@ -128,7 +128,7 @@ func main() { } }() - go events.PublishMetrics(clientset, js, clusterMetricsChan) + //go events.PublishMetrics(clientset, js, clusterMetricsChan) if cfg.KuberHealthyEnable { go kuberhealthy.StartKuberHealthy(js) } diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index dcf12bff..52fe0197 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -162,21 +162,21 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { // log.Println() // }, // }, - { - Subject: constants.KubvizSubject, - Consumer: cfg.KubvizConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.Metrics - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Kubviz Metrics Received: %#v,", metrics) - conn.InsertKubvizEvent(metrics) - log.Println() - }, - }, + // { + // Subject: constants.KubvizSubject, + // Consumer: cfg.KubvizConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.Metrics + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Kubviz Metrics Received: %#v,", metrics) + // conn.InsertKubvizEvent(metrics) + // log.Println() + // }, + // }, // { // Subject: constants.KUBESCORE_SUBJECT, // Consumer: cfg.KubscoreConsumer, From 710b78e180a3491d3e0d9faf9876357a12aff233 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 12:46:50 +0530 Subject: [PATCH 215/263] debug --- agent/kubviz/k8smetrics_agent.go | 20 ++--- client/pkg/clients/kubviz_client.go | 120 ++++++++++++++-------------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 47dd0571..daed474f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -21,12 +21,12 @@ import ( "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/agent/kubviz/plugins/events" "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + //"github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" - "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" - "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + //"github.com/intelops/kubviz/agent/kubviz/plugins/outdated" + //"github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" //"github.com/intelops/kubviz/agent/kubviz/plugins/trivy" "github.com/intelops/kubviz/agent/kubviz/scheduler" @@ -134,14 +134,14 @@ func main() { } go server.StartServer() collectAndPublishMetrics := func() { - err := outdated.OutDatedImages(config, js) - events.LogErr(err) - err = kubepreupgrade.KubePreUpgradeDetector(config, js) - events.LogErr(err) + // err := outdated.OutDatedImages(config, js) + // events.LogErr(err) + // err = kubepreupgrade.KubePreUpgradeDetector(config, js) + // events.LogErr(err) err = ketall.GetAllResources(config, js) events.LogErr(err) - err = rakkess.RakeesOutput(config, js) - events.LogErr(err) + // err = rakkess.RakeesOutput(config, js) + // events.LogErr(err) // err = trivy.RunTrivySbomScan(config, js) // events.LogErr(err) // err = trivy.RunTrivyImageScans(config, js) @@ -152,7 +152,7 @@ func main() { // events.LogErr(err) } - collectAndPublishMetrics() + //collectAndPublishMetrics() if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index 52fe0197..396a6024 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -55,21 +55,21 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, - { - Subject: constants.RakeesSubject, - Consumer: cfg.RakeesConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.RakeesMetrics - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Rakees Metrics Received: %#v,", metrics) - conn.InsertRakeesMetrics(metrics) - log.Println() - }, - }, + // { + // Subject: constants.RakeesSubject, + // Consumer: cfg.RakeesConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.RakeesMetrics + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Rakees Metrics Received: %#v,", metrics) + // conn.InsertRakeesMetrics(metrics) + // log.Println() + // }, + // }, { Subject: constants.KUBERHEALTHY_SUBJECT, Consumer: cfg.KuberhealthyConsumer, @@ -86,51 +86,51 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, - { - Subject: constants.OutdatedSubject, - Consumer: cfg.OutdatedConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.CheckResultfinal - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Outdated Metrics Received: %#v,", metrics) - conn.InsertOutdatedEvent(metrics) - log.Println() - }, - }, - { - Subject: constants.DeprecatedSubject, - Consumer: cfg.DeprecatedConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.DeprecatedAPI - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Deprecated API Metrics Received: %#v,", metrics) - conn.InsertDeprecatedAPI(metrics) - log.Println() - }, - }, - { - Subject: constants.DeletedSubject, - Consumer: cfg.DeletedConsumer, - Handler: func(msg *nats.Msg) { - msg.Ack() - var metrics model.DeletedAPI - err := json.Unmarshal(msg.Data, &metrics) - if err != nil { - log.Fatal(err) - } - log.Printf("Deleted API Metrics Received: %#v,", metrics) - conn.InsertDeletedAPI(metrics) - log.Println() - }, - }, + // { + // Subject: constants.OutdatedSubject, + // Consumer: cfg.OutdatedConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.CheckResultfinal + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Outdated Metrics Received: %#v,", metrics) + // conn.InsertOutdatedEvent(metrics) + // log.Println() + // }, + // }, + // { + // Subject: constants.DeprecatedSubject, + // Consumer: cfg.DeprecatedConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.DeprecatedAPI + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Deprecated API Metrics Received: %#v,", metrics) + // conn.InsertDeprecatedAPI(metrics) + // log.Println() + // }, + // }, + // { + // Subject: constants.DeletedSubject, + // Consumer: cfg.DeletedConsumer, + // Handler: func(msg *nats.Msg) { + // msg.Ack() + // var metrics model.DeletedAPI + // err := json.Unmarshal(msg.Data, &metrics) + // if err != nil { + // log.Fatal(err) + // } + // log.Printf("Deleted API Metrics Received: %#v,", metrics) + // conn.InsertDeletedAPI(metrics) + // log.Println() + // }, + // }, // { // Subject: constants.TRIVY_IMAGE_SUBJECT, // Consumer: cfg.TrivyImageConsumer, From cc3e442b57328439b2d8f9ad4a5139ca9184db74 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 12:55:29 +0530 Subject: [PATCH 216/263] debug --- agent/kubviz/plugins/kuberhealthy/kuberhealthy.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go index 2ae66ccd..90cf3c94 100644 --- a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go +++ b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go @@ -1,7 +1,7 @@ package kuberhealthy import ( - "context" + //"context" "encoding/json" "fmt" "io" @@ -11,10 +11,10 @@ import ( "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/constants" - "github.com/intelops/kubviz/pkg/opentelemetry" + //"github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kuberhealthy/kuberhealthy/v2/pkg/health" "github.com/nats-io/nats.go" - "go.opentelemetry.io/otel" + //"go.opentelemetry.io/otel" ) func StartKuberHealthy(js nats.JetStreamContext) { @@ -53,10 +53,10 @@ func pollAndPublishKuberhealthy(url string, js nats.JetStreamContext) error { } func PublishKuberhealthyMetrics(js nats.JetStreamContext, state health.State) error { - ctx := context.Background() - tracer := otel.Tracer("kuberhealthy") - _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishKuberhealthyMetrics") - defer span.End() + // ctx := context.Background() + // tracer := otel.Tracer("kuberhealthy") + // _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishKuberhealthyMetrics") + // defer span.End() metricsJSON, err := json.Marshal(state) if err != nil { From c4cd5b82f4e399804184da9065ca7ae68cfeca0b Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 13:13:50 +0530 Subject: [PATCH 217/263] debug --- pkg/opentelemetry/opentelemetry.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/pkg/opentelemetry/opentelemetry.go b/pkg/opentelemetry/opentelemetry.go index 1346bae2..201587b9 100644 --- a/pkg/opentelemetry/opentelemetry.go +++ b/pkg/opentelemetry/opentelemetry.go @@ -19,7 +19,7 @@ import ( type Configurations struct { ServiceName string `envconfig:"APPLICATION_NAME" default:"Kubviz"` CollectorURL string `envconfig:"OPTEL_URL" default:"otelcollector.azureagent.optimizor.app:80"` - IsEnabled bool `envconfig:"IS_OPTEL_ENABLED" default:"false"` + //IsEnabled bool `envconfig:"IS_OPTEL_ENABLED" default:"false"` } func GetConfigurations() (opteConfig *Configurations, err error) { @@ -33,23 +33,23 @@ func GetConfigurations() (opteConfig *Configurations, err error) { func InitTracer() (*sdktrace.TracerProvider, error) { ctx := context.Background() - config, err := GetConfigurations() + config, err := GetConfigurations() if err != nil { log.Println("Unable to read open telemetry configurations") return nil, err } - if !config.IsEnabled { - return nil, nil - } + // if !config.IsEnabled { + // return nil, nil + // } - headers := map[string]string{ + headers := map[string]string{ "signoz-service-name": config.ServiceName, } client := otlptracegrpc.NewClient( otlptracegrpc.WithEndpoint(config.CollectorURL), - otlptracegrpc.WithHeaders(headers), + otlptracegrpc.WithHeaders(headers), otlptracegrpc.WithInsecure(), ) @@ -59,20 +59,19 @@ func InitTracer() (*sdktrace.TracerProvider, error) { } res, err := resource.New( - ctx, - resource.WithAttributes( - attribute.String("service.name", config.ServiceName), + ctx, + resource.WithAttributes( + attribute.String("service.name", config.ServiceName), attribute.String("library.language", "go"), - - ), - ) + ), + ) if err != nil { log.Fatalf("failed to initialize resource: %e", err) } // Create the trace provider tp := sdktrace.NewTracerProvider( - trace.WithSampler(trace.AlwaysSample()), + trace.WithSampler(trace.AlwaysSample()), sdktrace.WithBatcher(exporter), sdktrace.WithResource(res), ) @@ -90,4 +89,4 @@ func InitTracer() (*sdktrace.TracerProvider, error) { func BuildContext(ctx context.Context) context.Context { newCtx, _ := context.WithCancel(ctx) return newCtx -} \ No newline at end of file +} From e728f070dcb773cfa940f391515987b8181137cc Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 13:59:04 +0530 Subject: [PATCH 218/263] debug --- client/pkg/clickhouse/db_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 60c50482..78e0b9a2 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -405,7 +405,7 @@ func (c *DBClient) InsertKuberhealthyMetrics(metrics health.State) { checkdata.CurrentUUID, checkdata.CurrentUUID, checkdata.OK, - checkdata.Errors, + checkdata.AuthoritativePod, checkdata.RunDuration, checkdata.Namespace, checkdata.Node, From 11b91a394d880d4c4a34b199a11c43dcaed74051 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Thu, 15 Feb 2024 14:57:03 +0530 Subject: [PATCH 219/263] debug --- client/pkg/clickhouse/db_client.go | 36 +++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 78e0b9a2..04ba75d7 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -400,17 +400,33 @@ func (c *DBClient) InsertKuberhealthyMetrics(metrics health.State) { log.Fatalf("error preparing statement: %v", err) } - for _, checkdata := range metrics.CheckDetails { + for checkName, checkdata := range metrics.CheckDetails { + ok := uint8(0) + if checkdata.OK { + ok = uint8(1) + } + errors := strings.Join(checkdata.Errors, ", ") + kcd := model.KuberhealthyCheckDetail{ + CurrentUUID: checkdata.CurrentUUID, + CheckName: checkName, + OK: ok, + Errors: errors, + RunDuration: checkdata.RunDuration, + Namespace: checkdata.Namespace, + Node: checkdata.Node, + LastRun: checkdata.LastRun.Time.UTC(), + AuthoritativePod: checkdata.AuthoritativePod, + } if _, err := stmt.Exec( - checkdata.CurrentUUID, - checkdata.CurrentUUID, - checkdata.OK, - checkdata.AuthoritativePod, - checkdata.RunDuration, - checkdata.Namespace, - checkdata.Node, - checkdata.LastRun, - checkdata.AuthoritativePod, + kcd.CurrentUUID, + kcd.CheckName, + kcd.OK, + kcd.Errors, + kcd.RunDuration, + kcd.Namespace, + kcd.Node, + kcd.LastRun, + kcd.AuthoritativePod, ); err != nil { log.Fatal(err) } From ab659c2fa17e3fded015f6cab4c09c3a51b1badd Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 16 Feb 2024 10:28:04 +0530 Subject: [PATCH 220/263] removed-logs-running-all service --- agent/kubviz/k8smetrics_agent.go | 44 +-- .../plugins/kuberhealthy/kuberhealthy.go | 14 +- client/pkg/clickhouse/db_client.go | 1 - client/pkg/clients/kubviz_client.go | 272 +++++++++--------- 4 files changed, 165 insertions(+), 166 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index daed474f..5617a21f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -21,14 +21,14 @@ import ( "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/agent/kubviz/plugins/events" "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" - //"github.com/intelops/kubviz/agent/kubviz/plugins/outdated" - //"github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" + "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" - //"github.com/intelops/kubviz/agent/kubviz/plugins/trivy" + "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" "github.com/intelops/kubviz/agent/kubviz/scheduler" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" @@ -64,7 +64,7 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - //clusterMetricsChan := make(chan error, 1) + clusterMetricsChan := make(chan error, 1) cfg, err := config.GetAgentConfigurations() if err != nil { log.Fatal("Failed to retrieve agent configurations", err) @@ -128,31 +128,31 @@ func main() { } }() - //go events.PublishMetrics(clientset, js, clusterMetricsChan) + go events.PublishMetrics(clientset, js, clusterMetricsChan) if cfg.KuberHealthyEnable { go kuberhealthy.StartKuberHealthy(js) } go server.StartServer() collectAndPublishMetrics := func() { - // err := outdated.OutDatedImages(config, js) - // events.LogErr(err) - // err = kubepreupgrade.KubePreUpgradeDetector(config, js) - // events.LogErr(err) + err := outdated.OutDatedImages(config, js) + events.LogErr(err) + err = kubepreupgrade.KubePreUpgradeDetector(config, js) + events.LogErr(err) err = ketall.GetAllResources(config, js) events.LogErr(err) - // err = rakkess.RakeesOutput(config, js) - // events.LogErr(err) - // err = trivy.RunTrivySbomScan(config, js) - // events.LogErr(err) - // err = trivy.RunTrivyImageScans(config, js) - // events.LogErr(err) - // err = trivy.RunTrivyK8sClusterScan(js) - // events.LogErr(err) - // err = kubescore.RunKubeScore(clientset, js) - // events.LogErr(err) + err = rakkess.RakeesOutput(config, js) + events.LogErr(err) + err = trivy.RunTrivySbomScan(config, js) + events.LogErr(err) + err = trivy.RunTrivyImageScans(config, js) + events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(js) + events.LogErr(err) + err = kubescore.RunKubeScore(clientset, js) + events.LogErr(err) } - //collectAndPublishMetrics() + collectAndPublishMetrics() if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) diff --git a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go index 90cf3c94..2ae66ccd 100644 --- a/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go +++ b/agent/kubviz/plugins/kuberhealthy/kuberhealthy.go @@ -1,7 +1,7 @@ package kuberhealthy import ( - //"context" + "context" "encoding/json" "fmt" "io" @@ -11,10 +11,10 @@ import ( "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/constants" - //"github.com/intelops/kubviz/pkg/opentelemetry" + "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kuberhealthy/kuberhealthy/v2/pkg/health" "github.com/nats-io/nats.go" - //"go.opentelemetry.io/otel" + "go.opentelemetry.io/otel" ) func StartKuberHealthy(js nats.JetStreamContext) { @@ -53,10 +53,10 @@ func pollAndPublishKuberhealthy(url string, js nats.JetStreamContext) error { } func PublishKuberhealthyMetrics(js nats.JetStreamContext, state health.State) error { - // ctx := context.Background() - // tracer := otel.Tracer("kuberhealthy") - // _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishKuberhealthyMetrics") - // defer span.End() + ctx := context.Background() + tracer := otel.Tracer("kuberhealthy") + _, span := tracer.Start(opentelemetry.BuildContext(ctx), "PublishKuberhealthyMetrics") + defer span.End() metricsJSON, err := json.Marshal(state) if err != nil { diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 04ba75d7..284b359a 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -388,7 +388,6 @@ func (c *DBClient) InsertKuberhealthyMetrics(metrics health.State) { _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertKuberhealthy") span.SetAttributes(attribute.String("kuberhealthy-client", "insert")) defer span.End() - log.Println("****metrics", metrics) tx, err := c.conn.Begin() if err != nil { diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index 396a6024..b126aaa7 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -55,21 +55,21 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, - // { - // Subject: constants.RakeesSubject, - // Consumer: cfg.RakeesConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.RakeesMetrics - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Rakees Metrics Received: %#v,", metrics) - // conn.InsertRakeesMetrics(metrics) - // log.Println() - // }, - // }, + { + Subject: constants.RakeesSubject, + Consumer: cfg.RakeesConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.RakeesMetrics + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Rakees Metrics Received: %#v,", metrics) + conn.InsertRakeesMetrics(metrics) + log.Println() + }, + }, { Subject: constants.KUBERHEALTHY_SUBJECT, Consumer: cfg.KuberhealthyConsumer, @@ -86,127 +86,127 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { log.Println() }, }, - // { - // Subject: constants.OutdatedSubject, - // Consumer: cfg.OutdatedConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.CheckResultfinal - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Outdated Metrics Received: %#v,", metrics) - // conn.InsertOutdatedEvent(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.DeprecatedSubject, - // Consumer: cfg.DeprecatedConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.DeprecatedAPI - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Deprecated API Metrics Received: %#v,", metrics) - // conn.InsertDeprecatedAPI(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.DeletedSubject, - // Consumer: cfg.DeletedConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.DeletedAPI - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Deleted API Metrics Received: %#v,", metrics) - // conn.InsertDeletedAPI(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.TRIVY_IMAGE_SUBJECT, - // Consumer: cfg.TrivyImageConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.TrivyImage - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Trivy Metrics Received: %#v,", metrics) - // conn.InsertTrivyImageMetrics(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.TRIVY_SBOM_SUBJECT, - // Consumer: cfg.TrivySbomConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.SbomData - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Println("failed to unmarshal from nats", err) - // return - // } - // log.Printf("Trivy sbom Metrics Received: %#v,", metrics) - // conn.InsertTrivySbomMetrics(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.KubvizSubject, - // Consumer: cfg.KubvizConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.Metrics - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Kubviz Metrics Received: %#v,", metrics) - // conn.InsertKubvizEvent(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.KUBESCORE_SUBJECT, - // Consumer: cfg.KubscoreConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.KubeScoreRecommendations - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Kubscore Metrics Received: %#v,", metrics) - // conn.InsertKubeScoreMetrics(metrics) - // log.Println() - // }, - // }, - // { - // Subject: constants.TRIVY_K8S_SUBJECT, - // Consumer: cfg.TrivyConsumer, - // Handler: func(msg *nats.Msg) { - // msg.Ack() - // var metrics model.Trivy - // err := json.Unmarshal(msg.Data, &metrics) - // if err != nil { - // log.Fatal(err) - // } - // log.Printf("Trivy Metrics Received: %#v,", metrics) - // conn.InsertTrivyMetrics(metrics) - // log.Println() - // }, - // }, + { + Subject: constants.OutdatedSubject, + Consumer: cfg.OutdatedConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.CheckResultfinal + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Outdated Metrics Received: %#v,", metrics) + conn.InsertOutdatedEvent(metrics) + log.Println() + }, + }, + { + Subject: constants.DeprecatedSubject, + Consumer: cfg.DeprecatedConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.DeprecatedAPI + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Deprecated API Metrics Received: %#v,", metrics) + conn.InsertDeprecatedAPI(metrics) + log.Println() + }, + }, + { + Subject: constants.DeletedSubject, + Consumer: cfg.DeletedConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.DeletedAPI + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Deleted API Metrics Received: %#v,", metrics) + conn.InsertDeletedAPI(metrics) + log.Println() + }, + }, + { + Subject: constants.TRIVY_IMAGE_SUBJECT, + Consumer: cfg.TrivyImageConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.TrivyImage + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Trivy Metrics Received: %#v,", metrics) + conn.InsertTrivyImageMetrics(metrics) + log.Println() + }, + }, + { + Subject: constants.TRIVY_SBOM_SUBJECT, + Consumer: cfg.TrivySbomConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.SbomData + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Println("failed to unmarshal from nats", err) + return + } + log.Printf("Trivy sbom Metrics Received: %#v,", metrics) + conn.InsertTrivySbomMetrics(metrics) + log.Println() + }, + }, + { + Subject: constants.KubvizSubject, + Consumer: cfg.KubvizConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.Metrics + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Kubviz Metrics Received: %#v,", metrics) + conn.InsertKubvizEvent(metrics) + log.Println() + }, + }, + { + Subject: constants.KUBESCORE_SUBJECT, + Consumer: cfg.KubscoreConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.KubeScoreRecommendations + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Kubscore Metrics Received: %#v,", metrics) + conn.InsertKubeScoreMetrics(metrics) + log.Println() + }, + }, + { + Subject: constants.TRIVY_K8S_SUBJECT, + Consumer: cfg.TrivyConsumer, + Handler: func(msg *nats.Msg) { + msg.Ack() + var metrics model.Trivy + err := json.Unmarshal(msg.Data, &metrics) + if err != nil { + log.Fatal(err) + } + log.Printf("Trivy Metrics Received: %#v,", metrics) + conn.InsertTrivyMetrics(metrics) + log.Println() + }, + }, } for _, sub := range subscriptions { From 9177c42ad392ceeca20ceb52ae6eb1e62da104af Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 16 Feb 2024 12:28:20 +0530 Subject: [PATCH 221/263] helm changes for kuberhealthy --- charts/client/Chart.yaml | 2 +- charts/client/templates/deployment.yaml | 2 ++ charts/client/values.yaml | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 1d01f41e..ebc9d780 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.18 +version: 1.1.19 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/deployment.yaml b/charts/client/templates/deployment.yaml index 27daebc0..2706a7fa 100644 --- a/charts/client/templates/deployment.yaml +++ b/charts/client/templates/deployment.yaml @@ -165,6 +165,8 @@ spec: value : {{ .Values.consumer.trivyimageconsumer }} - name : TRIVY_SBOM_CONSUMER value : {{ .Values.consumer.trivysbomconsumer }} + - name : KUBERHEALTHY_CONSUMER + value : {{ .Values.consumer.kuberhealthyconsumer }} resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 3e5add4f..d2186fd6 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -187,4 +187,5 @@ consumer: kubscoreconsumer: "KUBSCORE_CONSUMER" trivyconsumer: "TRIVY_CONSUMER" trivyimageconsumer: "TRIVY_IMAGE_CONSUMER" - trivysbomconsumer: "TRIVY_SBOM_CONSUMER" \ No newline at end of file + trivysbomconsumer: "TRIVY_SBOM_CONSUMER" + kuberhealthyconsumer: "KUBERHEALTHY_CONSUMER" \ No newline at end of file From a155f8f9e98b8d9c855409925fead229f5700b1e Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Fri, 16 Feb 2024 12:51:00 +0530 Subject: [PATCH 222/263] store expire data --- client/pkg/application/application.go | 99 +++++++++++++++++++- client/pkg/config/config.go | 38 ++++---- client/pkg/storage/store.go | 125 ++++++++++++++++++++++++++ go.mod | 2 + go.sum | 17 ++++ sql/0000010_trivy_misconfig.up.sql | 3 +- sql/0000011_trivyimage.up.sql | 3 +- sql/0000012_dockerhubbuild.up.sql | 3 +- sql/0000013_azurecontainerpush.up.sql | 3 +- sql/0000014_quaycontainerpush.up.sql | 3 +- sql/0000015_trivysbom.up.sql | 3 +- sql/0000016_azure_devops.up.sql | 3 +- sql/0000017_github.up.sql | 3 +- sql/0000018_gitlab.up.sql | 3 +- sql/0000019_bitbucket.up.sql | 3 +- sql/000001_events.up.sql | 3 +- sql/0000020_gitea.up.sql | 3 +- sql/0000021_kuberhealthy.up.sql | 3 +- sql/000002_rakkess.up.sql | 3 +- sql/000003_DeprecatedAPIs.up.sql | 3 +- sql/000004_DeletedAPIs.up.sql | 3 +- sql/000005_jfrogcontainerpush.up.sql | 3 +- sql/000006_getall_resources.up.sql | 3 +- sql/000007_outdated_images.up.sql | 3 +- sql/000008_kubescore.up.sql | 3 +- sql/000009_trivy_vul.up.sql | 3 +- 26 files changed, 304 insertions(+), 40 deletions(-) create mode 100644 client/pkg/storage/store.go diff --git a/client/pkg/application/application.go b/client/pkg/application/application.go index 4b78b3d8..6d9c3c99 100644 --- a/client/pkg/application/application.go +++ b/client/pkg/application/application.go @@ -2,13 +2,19 @@ package application import ( "context" + "database/sql" "log" + "os" + "os/signal" + "syscall" "github.com/intelops/kubviz/client/pkg/clickhouse" "github.com/intelops/kubviz/client/pkg/clients" "github.com/intelops/kubviz/client/pkg/config" + "github.com/intelops/kubviz/client/pkg/storage" "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/kelseyhightower/envconfig" + "github.com/robfig/cron/v3" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" ) @@ -19,20 +25,44 @@ type Application struct { dbClient clickhouse.DBInterface } +const ( + EventsTable = "events" + RakkessTable = "rakkess" + DeprecatedAPIsTable = "DeprecatedAPIs" + DeletedAPIsTable = "DeletedAPIs" + JfrogContainerPushTable = "jfrogcontainerpush" + GetAllResourcesTable = "getall_resources" + OutdatedImagesTable = "outdated_images" + KubeScoreTable = "kubescore" + TrivyVulTable = "trivy_vul" + TrivyMisconfigTable = "trivy_misconfig" + TrivyImageTable = "trivyimage" + DockerHubBuildTable = "dockerhubbuild" + AzureContainerPushTable = "azurecontainerpush" + QuayContainerPushTable = "quaycontainerpush" + TrivySBOMTable = "trivysbom" + AzureDevOpsTable = "azure_devops" + GitHubTable = "github" + GitLabTable = "gitlab" + BitbucketTable = "bitbucket" + GiteaTable = "gitea" + KuberHealthy = "kuberhealthy" +) + func Start() *Application { log.Println("Client Application started...") - ctx:=context.Background() + ctx := context.Background() tracer := otel.Tracer("kubviz-client") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "Start") span.SetAttributes(attribute.String("start-app-client", "application")) defer span.End() - + cfg := &config.Config{} if err := envconfig.Process("", cfg); err != nil { log.Fatalf("Could not parse env Config: %v", err) } - dbClient, _, err := clickhouse.NewDBClient(cfg) + dbClient, conn, err := clickhouse.NewDBClient(cfg) if err != nil { log.Fatal(err) } @@ -41,6 +71,53 @@ func Start() *Application { if err != nil { log.Fatal("Error establishing connection to NATS:", err) } + // c := cron.New() + // _, err = c.AddFunc("@daily", func() { + // if err := exportDataForTables(conn); err != nil { + // log.Println("Error exporting data:", err) + // } + // }) + // if err != nil { + // log.Fatal("Error adding cron job:", err) + // } + + // // Listen for interrupt signals to stop the program + // interrupt := make(chan os.Signal, 1) + // signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + + // // Start the cron job scheduler + // c.Start() + + // // Wait for an interrupt signal to stop the program + // <-interrupt + + // // Stop the cron scheduler gracefully + // c.Stop() + if cfg.AwsEnable { + c := cron.New() + _, err = c.AddFunc("@daily", func() { + if err := exportDataForTables(conn); err != nil { + log.Println("Error exporting data:", err) + } + }) + if err != nil { + log.Fatal("Error adding cron job:", err) + } + + // Listen for interrupt signals to stop the program + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + + // Start the cron job scheduler + c.Start() + + // Wait for an interrupt signal to stop the program + <-interrupt + + // Stop the cron scheduler gracefully + c.Stop() + } + return &Application{ Config: cfg, conn: natsContext, @@ -53,3 +130,19 @@ func (app *Application) Close() { app.conn.Close() app.dbClient.Close() } +func exportDataForTables(db *sql.DB) error { + //pvcMountPath := "/mnt/client/kbz" + tables := []string{ + EventsTable, RakkessTable, DeprecatedAPIsTable, DeletedAPIsTable, JfrogContainerPushTable, GetAllResourcesTable, OutdatedImagesTable, KubeScoreTable, TrivyVulTable, TrivyMisconfigTable, TrivyImageTable, DockerHubBuildTable, AzureContainerPushTable, QuayContainerPushTable, TrivySBOMTable, AzureDevOpsTable, GitHubTable, GitLabTable, BitbucketTable, KuberHealthy, GiteaTable, + } + for _, tableName := range tables { + err := storage.ExportExpiredData(tableName, db) + if err != nil { + log.Printf("Error exporting data for table %s: %v", tableName, err) + } else { + log.Printf("Export completed successfully for table %s.\n", tableName) + } + } + + return nil +} diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index 7fb49a2c..d0dceff1 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -1,21 +1,27 @@ package config type Config struct { - NatsAddress string `envconfig:"NATS_ADDRESS"` - NatsToken string `envconfig:"NATS_TOKEN"` - DbPort int `envconfig:"DB_PORT"` - DBAddress string `envconfig:"DB_ADDRESS"` - ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` - ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` - KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER" required:"true"` - RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER" required:"true"` - OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER" required:"true"` - DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER" required:"true"` - DeletedConsumer string `envconfig:"DELETED_API_CONSUMER" required:"true"` - KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER" required:"true"` - KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER" required:"true"` - TrivyConsumer string `envconfig:"TRIVY_CONSUMER" required:"true"` - TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER" required:"true"` - TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER" required:"true"` + NatsAddress string `envconfig:"NATS_ADDRESS"` + NatsToken string `envconfig:"NATS_TOKEN"` + DbPort int `envconfig:"DB_PORT"` + DBAddress string `envconfig:"DB_ADDRESS"` + ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` + ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` + KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER" required:"true"` + RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER" required:"true"` + OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER" required:"true"` + DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER" required:"true"` + DeletedConsumer string `envconfig:"DELETED_API_CONSUMER" required:"true"` + KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER" required:"true"` + KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER" required:"true"` + TrivyConsumer string `envconfig:"TRIVY_CONSUMER" required:"true"` + TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER" required:"true"` + TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER" required:"true"` KuberhealthyConsumer string `envconfig:"KUBERHEALTHY_CONSUMER" required:"true"` + AwsEnable bool `envconfig:"AWS_ENABLE" default:"false"` + AWSRegion string `envconfig:"AWS_REGION"` + AWSAccessKey string `envconfig:"AWS_ACCESS_KEY"` + AWSSecretKey string `envconfig:"AWS_SECRET_KEY"` + S3BucketName string `envconfig:"S3_BUCKET_NAME"` + S3ObjectKey string `envconfig:"S3_OBJECT_KEY"` } diff --git a/client/pkg/storage/store.go b/client/pkg/storage/store.go new file mode 100644 index 00000000..b5684c6b --- /dev/null +++ b/client/pkg/storage/store.go @@ -0,0 +1,125 @@ +package storage + +import ( + "database/sql" + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/intelops/kubviz/client/pkg/config" + "github.com/kelseyhightower/envconfig" +) + +func ExportExpiredData(tableName string, db *sql.DB) error { + columns, err := getTableColumns(db, tableName) + if err != nil { + return fmt.Errorf("error getting columns for table %s: %v", tableName, err) + } + + // Construct SQL query + query := fmt.Sprintf("SELECT * FROM %s WHERE ExportedAt IS NULL", tableName) + + // Query expired data + rows, err := db.Query(query) + if err != nil { + return fmt.Errorf("error querying ClickHouse: %v", err) + } + defer rows.Close() + + // Construct CSV data in memory + var csvData strings.Builder + csvData.WriteString(columns + "\n") // Write CSV header + + for rows.Next() { + // Assuming a dynamic structure, scan the columns into a slice of interface{} + columnValues := make([]interface{}, len(strings.Split(columns, ","))) + for i := range columnValues { + columnValues[i] = new(interface{}) + } + + err := rows.Scan(columnValues...) + if err != nil { + return fmt.Errorf("error scanning ClickHouse row: %v", err) + } + + // Write the values to the CSV data + var rowData []string + for _, value := range columnValues { + // Dereference the pointer to get the interface{} value, then format it as a string + rowData = append(rowData, fmt.Sprintf("%v", *value.(*interface{}))) + } + csvData.WriteString(strings.Join(rowData, ",") + "\n") + } + + // Upload the CSV data to S3 + err = uploadToS3(&csvData, fmt.Sprintf("exported_data_%s.csv", tableName)) + if err != nil { + return fmt.Errorf("error uploading CSV to S3: %v", err) + } + + // Update ExportedAt column with the current timestamp for exported rows + updateQuery := fmt.Sprintf("ALTER TABLE %s UPDATE ExportedAt = now() WHERE ExportedAt IS NULL", tableName) + _, err = db.Exec(updateQuery) + if err != nil { + return fmt.Errorf("error updating ExportedAt column: %v", err) + } + + return nil +} + +func getTableColumns(db *sql.DB, tableName string) (string, error) { + // Query to get column names + query := fmt.Sprintf("DESCRIBE TABLE %s", tableName) + rows, err := db.Query(query) + if err != nil { + return "", err + } + defer rows.Close() + + // Get column names + var columns []string + for rows.Next() { + var columnName string + rows.Scan(&columnName) + columns = append(columns, columnName) + } + + return strings.Join(columns, ","), nil +} + +func uploadToS3(csvData *strings.Builder, s3ObjectKey string) error { + cfg := &config.Config{} + if err := envconfig.Process("", cfg); err != nil { + log.Fatalf("Could not parse env Config: %v", err) + } + + // Set up AWS S3 session + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(cfg.AWSRegion), + Credentials: credentials.NewStaticCredentials(cfg.AWSAccessKey, cfg.AWSSecretKey, ""), + }) + if err != nil { + return fmt.Errorf("error creating S3 session: %v", err) + } + + // Create an S3 service client + s3Client := s3.New(sess) + + // Upload the CSV data to S3 + _, err = s3Client.PutObject(&s3.PutObjectInput{ + Bucket: aws.String(cfg.S3BucketName), + Key: aws.String((s3ObjectKey)), + Body: strings.NewReader(csvData.String()), + }) + if err != nil { + return fmt.Errorf("error uploading data to S3: %v", err) + } + + fmt.Printf("Data uploaded to S3: %s\n", s3ObjectKey) + + return nil +} diff --git a/go.mod b/go.mod index fa52bd8c..cbb62157 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/99designs/gqlgen v0.17.42 github.com/ClickHouse/clickhouse-go/v2 v2.10.1 github.com/aquasecurity/trivy v0.43.1 + github.com/aws/aws-sdk-go v1.44.245 github.com/blang/semver v3.5.1+incompatible github.com/corneliusweig/tabwriter v0.0.0-20190512204542-5f8a091e83b5 github.com/davecgh/go-spew v1.1.1 @@ -97,6 +98,7 @@ require ( github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.16.5 // indirect diff --git a/go.sum b/go.sum index 9aaeb1a6..4e586f75 100644 --- a/go.sum +++ b/go.sum @@ -115,6 +115,7 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/aws/aws-sdk-go v1.25.24/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.245 h1:KtY2s4q31/kn33AdV63R5t77mdxsI7rq3YT7Mgo805M= +github.com/aws/aws-sdk-go v1.44.245/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -422,6 +423,9 @@ github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdi github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= 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= @@ -676,6 +680,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0= github.com/zegl/kube-score v1.17.0 h1:vedzK0pm5yOb1ocm5gybMNYsJRG8iTAatbo3LFIWbUc= @@ -731,6 +736,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= @@ -771,6 +777,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.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.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -814,6 +821,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -841,6 +850,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -895,15 +905,20 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -915,6 +930,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -976,6 +992,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= 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= diff --git a/sql/0000010_trivy_misconfig.up.sql b/sql/0000010_trivy_misconfig.up.sql index 89d758a5..978a9ff8 100644 --- a/sql/0000010_trivy_misconfig.up.sql +++ b/sql/0000010_trivy_misconfig.up.sql @@ -15,7 +15,8 @@ CREATE TABLE IF NOT EXISTS trivy_misconfig ( misconfig_severity String, misconfig_status String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000011_trivyimage.up.sql b/sql/0000011_trivyimage.up.sql index 572bb2b4..3787fa59 100644 --- a/sql/0000011_trivyimage.up.sql +++ b/sql/0000011_trivyimage.up.sql @@ -11,7 +11,8 @@ CREATE TABLE IF NOT EXISTS trivyimage ( vul_severity String, vul_published_date DateTime('UTC'), vul_last_modified_date DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000012_dockerhubbuild.up.sql b/sql/0000012_dockerhubbuild.up.sql index 0485e52b..a448994e 100644 --- a/sql/0000012_dockerhubbuild.up.sql +++ b/sql/0000012_dockerhubbuild.up.sql @@ -6,7 +6,8 @@ CREATE TABLE IF NOT EXISTS dockerhubbuild ( Owner String, Event String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000013_azurecontainerpush.up.sql b/sql/0000013_azurecontainerpush.up.sql index a0f5916d..6dbb5aa2 100644 --- a/sql/0000013_azurecontainerpush.up.sql +++ b/sql/0000013_azurecontainerpush.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS azurecontainerpush ( Size Int32, SHAID String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; \ No newline at end of file diff --git a/sql/0000014_quaycontainerpush.up.sql b/sql/0000014_quaycontainerpush.up.sql index 2c249b56..6304fcc7 100644 --- a/sql/0000014_quaycontainerpush.up.sql +++ b/sql/0000014_quaycontainerpush.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS quaycontainerpush ( tag String, Event String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index 0e3a9c2e..7bc749ac 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -8,7 +8,8 @@ CREATE TABLE IF NOT EXISTS trivysbom ( serial_number String, version INTEGER, bom_format String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000016_azure_devops.up.sql b/sql/0000016_azure_devops.up.sql index 06a32dd2..bc7752a8 100644 --- a/sql/0000016_azure_devops.up.sql +++ b/sql/0000016_azure_devops.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS azure_devops ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000017_github.up.sql b/sql/0000017_github.up.sql index 96d9bf24..2f627dd5 100644 --- a/sql/0000017_github.up.sql +++ b/sql/0000017_github.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS github ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000018_gitlab.up.sql b/sql/0000018_gitlab.up.sql index 2403dff1..d47dd988 100644 --- a/sql/0000018_gitlab.up.sql +++ b/sql/0000018_gitlab.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS gitlab ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000019_bitbucket.up.sql b/sql/0000019_bitbucket.up.sql index adf08956..778e2ea7 100644 --- a/sql/0000019_bitbucket.up.sql +++ b/sql/0000019_bitbucket.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS bitbucket ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000001_events.up.sql b/sql/000001_events.up.sql index 2ba1313f..d165653f 100644 --- a/sql/000001_events.up.sql +++ b/sql/000001_events.up.sql @@ -13,7 +13,8 @@ CREATE TABLE IF NOT EXISTS events ( ImageName String, FirstTime String, LastTime String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000020_gitea.up.sql b/sql/0000020_gitea.up.sql index 1b42c0e7..3232bc56 100644 --- a/sql/0000020_gitea.up.sql +++ b/sql/0000020_gitea.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS gitea ( RepoName String, TimeStamp DateTime('UTC'), Event String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000021_kuberhealthy.up.sql b/sql/0000021_kuberhealthy.up.sql index 62ce6e51..3462ca6c 100644 --- a/sql/0000021_kuberhealthy.up.sql +++ b/sql/0000021_kuberhealthy.up.sql @@ -8,7 +8,8 @@ CREATE TABLE IF NOT EXISTS kuberhealthy ( Node String, LastRun DateTime('UTC'), AuthoritativePod String, - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000002_rakkess.up.sql b/sql/000002_rakkess.up.sql index 3542f59b..0bbd400a 100644 --- a/sql/000002_rakkess.up.sql +++ b/sql/000002_rakkess.up.sql @@ -6,7 +6,8 @@ CREATE TABLE IF NOT EXISTS rakkess ( List String, Update String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000003_DeprecatedAPIs.up.sql b/sql/000003_DeprecatedAPIs.up.sql index a7ed1b6e..46be7b93 100644 --- a/sql/000003_DeprecatedAPIs.up.sql +++ b/sql/000003_DeprecatedAPIs.up.sql @@ -6,7 +6,8 @@ CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( Deprecated UInt8, Scope String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000004_DeletedAPIs.up.sql b/sql/000004_DeletedAPIs.up.sql index 931b93e7..d333b3a4 100644 --- a/sql/000004_DeletedAPIs.up.sql +++ b/sql/000004_DeletedAPIs.up.sql @@ -8,7 +8,8 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( Deleted UInt8, Scope String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000005_jfrogcontainerpush.up.sql b/sql/000005_jfrogcontainerpush.up.sql index 6bac6d8d..a3a98fa6 100644 --- a/sql/000005_jfrogcontainerpush.up.sql +++ b/sql/000005_jfrogcontainerpush.up.sql @@ -9,7 +9,8 @@ CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( Tag String, Event String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000006_getall_resources.up.sql b/sql/000006_getall_resources.up.sql index 23991a21..32168213 100644 --- a/sql/000006_getall_resources.up.sql +++ b/sql/000006_getall_resources.up.sql @@ -5,7 +5,8 @@ CREATE TABLE IF NOT EXISTS getall_resources ( Resource String, Age String, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000007_outdated_images.up.sql b/sql/000007_outdated_images.up.sql index 479c0902..9779aead 100644 --- a/sql/000007_outdated_images.up.sql +++ b/sql/000007_outdated_images.up.sql @@ -7,7 +7,8 @@ CREATE TABLE IF NOT EXISTS outdated_images ( LatestVersion String, VersionsBehind Int64, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000008_kubescore.up.sql b/sql/000008_kubescore.up.sql index b6ee3e3f..2db441b1 100644 --- a/sql/000008_kubescore.up.sql +++ b/sql/000008_kubescore.up.sql @@ -13,7 +13,8 @@ CREATE TABLE IF NOT EXISTS kubescore ( file_name String, file_row BIGINT, EventTime DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000009_trivy_vul.up.sql b/sql/000009_trivy_vul.up.sql index 8acb241d..2ebc670f 100644 --- a/sql/000009_trivy_vul.up.sql +++ b/sql/000009_trivy_vul.up.sql @@ -15,7 +15,8 @@ CREATE TABLE IF NOT EXISTS trivy_vul ( vul_severity String, vul_published_date DateTime('UTC'), vul_last_modified_date DateTime('UTC'), - ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}} + ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, + ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() ORDER BY ExpiryDate TTL ExpiryDate; From c751564e9a6b68a9dcca718c735c3a488f03ccd3 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Mon, 19 Feb 2024 15:42:50 +0530 Subject: [PATCH 223/263] added events handler in graphql --- graphqlserver/graph/generated.go | 200 ++++++++++++++++++++++++ graphqlserver/graph/model/models_gen.go | 1 + graphqlserver/graph/schema.graphqls | 35 +++-- graphqlserver/graph/schema.resolvers.go | 37 +++++ steps-to-test.txt | 27 ++++ 5 files changed, 284 insertions(+), 16 deletions(-) diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index 6916e996..c8922abe 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -99,6 +99,7 @@ type ComplexityRoot struct { FirstTime func(childComplexity int) int Host func(childComplexity int) int ID func(childComplexity int) int + ImageName func(childComplexity int) int Kind func(childComplexity int) int LastTime func(childComplexity int) int Message func(childComplexity int) int @@ -191,6 +192,7 @@ type ComplexityRoot struct { AllTrivyMisconfigs func(childComplexity int) int AllTrivySBOMs func(childComplexity int) int AllTrivyVuls func(childComplexity int) int + EventsByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int OutdatedImagesByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int OutdatedImagesCount func(childComplexity int, clusterName string, namespace string) int UniqueClusters func(childComplexity int) int @@ -307,6 +309,7 @@ type QueryResolver interface { AllClusterDeprecatedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) AllClusterDeletedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) AllClusterNamespaceResourceCounts(ctx context.Context) ([]*model.ClusterNamespaceResourceCount, error) + EventsByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.Event, error) } type executableSchema struct { @@ -566,6 +569,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Event.ID(childComplexity), true + case "Event.ImageName": + if e.complexity.Event.ImageName == nil { + break + } + + return e.complexity.Event.ImageName(childComplexity), true + case "Event.Kind": if e.complexity.Event.Kind == nil { break @@ -1063,6 +1073,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.AllTrivyVuls(childComplexity), true + case "Query.eventsByClusterAndNamespace": + if e.complexity.Query.EventsByClusterAndNamespace == nil { + break + } + + args, err := ec.field_Query_eventsByClusterAndNamespace_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.EventsByClusterAndNamespace(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + case "Query.outdatedImagesByClusterAndNamespace": if e.complexity.Query.OutdatedImagesByClusterAndNamespace == nil { break @@ -1721,6 +1743,30 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Query_eventsByClusterAndNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 + return args, nil +} + func (ec *executionContext) field_Query_outdatedImagesByClusterAndNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -3392,6 +3438,47 @@ func (ec *executionContext) fieldContext_Event_Event(ctx context.Context, field return fc, nil } +func (ec *executionContext) _Event_ImageName(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_ImageName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ImageName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Event_ImageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Event", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Event_FirstTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Event_FirstTime(ctx, field) if err != nil { @@ -5785,6 +5872,8 @@ func (ec *executionContext) fieldContext_Query_allEvents(ctx context.Context, fi return ec.fieldContext_Event_Host(ctx, field) case "Event": return ec.fieldContext_Event_Event(ctx, field) + case "ImageName": + return ec.fieldContext_Event_ImageName(ctx, field) case "FirstTime": return ec.fieldContext_Event_FirstTime(ctx, field) case "LastTime": @@ -6850,6 +6939,93 @@ func (ec *executionContext) fieldContext_Query_allClusterNamespaceResourceCounts return fc, nil } +func (ec *executionContext) _Query_eventsByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_eventsByClusterAndNamespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().EventsByClusterAndNamespace(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Event) + fc.Result = res + return ec.marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_eventsByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_Event_ClusterName(ctx, field) + case "Id": + return ec.fieldContext_Event_Id(ctx, field) + case "EventTime": + return ec.fieldContext_Event_EventTime(ctx, field) + case "OpType": + return ec.fieldContext_Event_OpType(ctx, field) + case "Name": + return ec.fieldContext_Event_Name(ctx, field) + case "Namespace": + return ec.fieldContext_Event_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_Event_Kind(ctx, field) + case "Message": + return ec.fieldContext_Event_Message(ctx, field) + case "Reason": + return ec.fieldContext_Event_Reason(ctx, field) + case "Host": + return ec.fieldContext_Event_Host(ctx, field) + case "Event": + return ec.fieldContext_Event_Event(ctx, field) + case "ImageName": + return ec.fieldContext_Event_ImageName(ctx, field) + case "FirstTime": + return ec.fieldContext_Event_FirstTime(ctx, field) + case "LastTime": + return ec.fieldContext_Event_LastTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_Event_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Event", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_eventsByClusterAndNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -12019,6 +12195,8 @@ func (ec *executionContext) _Event(ctx context.Context, sel ast.SelectionSet, ob out.Values[i] = ec._Event_Host(ctx, field, obj) case "Event": out.Values[i] = ec._Event_Event(ctx, field, obj) + case "ImageName": + out.Values[i] = ec._Event_ImageName(ctx, field, obj) case "FirstTime": out.Values[i] = ec._Event_FirstTime(ctx, field, obj) case "LastTime": @@ -12870,6 +13048,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "eventsByClusterAndNamespace": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_eventsByClusterAndNamespace(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index 1aff036e..0048aa56 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -59,6 +59,7 @@ type Event struct { Reason *string `json:"Reason,omitempty"` Host *string `json:"Host,omitempty"` Event *string `json:"Event,omitempty"` + ImageName *string `json:"ImageName,omitempty"` FirstTime *string `json:"FirstTime,omitempty"` LastTime *string `json:"LastTime,omitempty"` ExpiryDate *string `json:"ExpiryDate,omitempty"` diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index 37783b48..f96ea03b 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -18,6 +18,24 @@ type Query { allClusterDeprecatedAPIsCounts: [ClusterAPIsCount!]! allClusterDeletedAPIsCounts: [ClusterAPIsCount!]! allClusterNamespaceResourceCounts: [ClusterNamespaceResourceCount!]! + eventsByClusterAndNamespace(clusterName: String!, namespace: String!): [Event!]! +} +type Event { + ClusterName: String + Id: String + EventTime: String + OpType: String + Name: String + Namespace: String + Kind: String + Message: String + Reason: String + Host: String + Event: String + ImageName: String + FirstTime: String + LastTime: String + ExpiryDate: String } type Namespace { @@ -210,22 +228,7 @@ type Resource { eventTime: String! } -type Event { - ClusterName: String - Id: String - EventTime: String - OpType: String - Name: String - Namespace: String - Kind: String - Message: String - Reason: String - Host: String - Event: String - FirstTime: String - LastTime: String - ExpiryDate: String -} + type Rakkess { ClusterName: String diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index e6229bc9..49e2d694 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -620,6 +620,43 @@ func (r *queryResolver) AllClusterNamespaceResourceCounts(ctx context.Context) ( return results, nil } +// EventsByClusterAndNamespace is the resolver for the eventsByClusterAndNamespace field. +func (r *queryResolver) EventsByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.Event, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := `SELECT ClusterName, Id, EventTime, OpType, Name, Namespace, Kind, Message, Reason, Host, Event, ImageName, FirstTime, LastTime FROM events WHERE ClusterName = ? AND Namespace = ?` + + rows, err := r.DB.QueryContext(ctx, query, clusterName, namespace) + if err != nil { + if err == sql.ErrNoRows { + return []*model.Event{}, nil + } + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var events []*model.Event + for rows.Next() { + var e model.Event + if err := rows.Scan(&e.ClusterName, &e.ID, &e.EventTime, &e.OpType, &e.Name, &e.Namespace, &e.Kind, &e.Message, &e.Reason, &e.Host, &e.Event, &e.ImageName, &e.FirstTime, &e.LastTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + events = append(events, &e) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return events, nil +} + // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } diff --git a/steps-to-test.txt b/steps-to-test.txt index 6e2b1af7..f34bdfdd 100644 --- a/steps-to-test.txt +++ b/steps-to-test.txt @@ -135,3 +135,30 @@ query { } ... + + + + resources - clusterName, namespace + + + MOM: + 1. filter by cluster name in the unique namespaces. - Vijesh + 2.Role id - is single (it is missed in create group request) - Anila + 3. Total no of users in group response is missing - get group - Nithu + 4. Role details missing in get group users response - Nithu + 5. Update group --> combine both the apis - vijesh (need to discuss with Iyappan) + + + + DONE: + outdated + events + + NOT DONE: + trivy_vul - + trivy_misconfig + trivyimage - only cluster name filter + kubescore + getall_resources + DeletedAPIs - by cluster name + depricated - by cluster name From 4bafdeecc52b0a5db6a554f0d1f280987fb64160 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Tue, 20 Feb 2024 09:38:22 +0530 Subject: [PATCH 224/263] new uniqueNamespaces handler with clustername filter --- client/pkg/config/config.go | 32 ++--- graphqlserver/graph/generated.go | 178 +++++------------------- graphqlserver/graph/schema.graphqls | 3 +- graphqlserver/graph/schema.resolvers.go | 45 +----- graphqlserver/graph/utils.go | 7 +- 5 files changed, 58 insertions(+), 207 deletions(-) diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index 7fb49a2c..92c9dc72 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -1,21 +1,21 @@ package config type Config struct { - NatsAddress string `envconfig:"NATS_ADDRESS"` - NatsToken string `envconfig:"NATS_TOKEN"` - DbPort int `envconfig:"DB_PORT"` - DBAddress string `envconfig:"DB_ADDRESS"` - ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` - ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` - KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER" required:"true"` - RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER" required:"true"` - OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER" required:"true"` - DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER" required:"true"` - DeletedConsumer string `envconfig:"DELETED_API_CONSUMER" required:"true"` - KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER" required:"true"` - KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER" required:"true"` - TrivyConsumer string `envconfig:"TRIVY_CONSUMER" required:"true"` - TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER" required:"true"` - TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER" required:"true"` + NatsAddress string `envconfig:"NATS_ADDRESS"` + NatsToken string `envconfig:"NATS_TOKEN"` + DbPort int `envconfig:"DB_PORT"` + DBAddress string `envconfig:"DB_ADDRESS"` + ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` + ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` + KetallConsumer string `envconfig:"KETALL_EVENTS_CONSUMER" required:"true"` + RakeesConsumer string `envconfig:"RAKEES_METRICS_CONSUMER" required:"true"` + OutdatedConsumer string `envconfig:"OUTDATED_EVENTS_CONSUMER" required:"true"` + DeprecatedConsumer string `envconfig:"DEPRECATED_API_CONSUMER" required:"true"` + DeletedConsumer string `envconfig:"DELETED_API_CONSUMER" required:"true"` + KubvizConsumer string `envconfig:"KUBVIZ_EVENTS_CONSUMER" required:"true"` + KubscoreConsumer string `envconfig:"KUBSCORE_CONSUMER" required:"true"` + TrivyConsumer string `envconfig:"TRIVY_CONSUMER" required:"true"` + TrivyImageConsumer string `envconfig:"TRIVY_IMAGE_CONSUMER" required:"true"` + TrivySbomConsumer string `envconfig:"TRIVY_SBOM_CONSUMER" required:"true"` KuberhealthyConsumer string `envconfig:"KUBERHEALTHY_CONSUMER" required:"true"` } diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index c8922abe..7a5b07b3 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -186,7 +186,6 @@ type ComplexityRoot struct { AllEvents func(childComplexity int) int AllGetAllResources func(childComplexity int) int AllKubeScores func(childComplexity int) int - AllNamespaceData func(childComplexity int) int AllRakkess func(childComplexity int) int AllTrivyImages func(childComplexity int) int AllTrivyMisconfigs func(childComplexity int) int @@ -196,7 +195,7 @@ type ComplexityRoot struct { OutdatedImagesByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int OutdatedImagesCount func(childComplexity int, clusterName string, namespace string) int UniqueClusters func(childComplexity int) int - UniqueNamespaces func(childComplexity int) int + UniqueNamespaces func(childComplexity int, clusterName string) int } Rakkess struct { @@ -290,7 +289,6 @@ type ComplexityRoot struct { } type QueryResolver interface { - AllNamespaceData(ctx context.Context) ([]*model.NamespaceData, error) AllEvents(ctx context.Context) ([]*model.Event, error) AllRakkess(ctx context.Context) ([]*model.Rakkess, error) AllDeprecatedAPIs(ctx context.Context) ([]*model.DeprecatedAPI, error) @@ -301,7 +299,7 @@ type QueryResolver interface { AllKubeScores(ctx context.Context) ([]*model.Kubescore, error) AllTrivyVuls(ctx context.Context) ([]*model.TrivyVul, error) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyMisconfig, error) - UniqueNamespaces(ctx context.Context) ([]*model.Namespace, error) + UniqueNamespaces(ctx context.Context, clusterName string) ([]*model.Namespace, error) UniqueClusters(ctx context.Context) ([]*model.Cluster, error) OutdatedImagesByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.OutdatedImage, error) OutdatedImagesCount(ctx context.Context, clusterName string, namespace string) (int, error) @@ -1031,13 +1029,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.AllKubeScores(childComplexity), true - case "Query.allNamespaceData": - if e.complexity.Query.AllNamespaceData == nil { - break - } - - return e.complexity.Query.AllNamespaceData(childComplexity), true - case "Query.allRakkess": if e.complexity.Query.AllRakkess == nil { break @@ -1121,7 +1112,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - return e.complexity.Query.UniqueNamespaces(childComplexity), true + args, err := ec.field_Query_uniqueNamespaces_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.UniqueNamespaces(childComplexity, args["clusterName"].(string)), true case "Rakkess.ClusterName": if e.complexity.Rakkess.ClusterName == nil { @@ -1815,6 +1811,21 @@ func (ec *executionContext) field_Query_outdatedImagesCount_args(ctx context.Con return args, nil } +func (ec *executionContext) field_Query_uniqueNamespaces_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + return args, nil +} + func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -5757,60 +5768,6 @@ func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Con return fc, nil } -func (ec *executionContext) _Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allNamespaceData(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllNamespaceData(rctx) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.([]*model.NamespaceData) - fc.Result = res - return ec.marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Query_allNamespaceData(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Query", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "namespace": - return ec.fieldContext_NamespaceData_namespace(ctx, field) - case "outdatedImages": - return ec.fieldContext_NamespaceData_outdatedImages(ctx, field) - case "kubeScores": - return ec.fieldContext_NamespaceData_kubeScores(ctx, field) - case "resources": - return ec.fieldContext_NamespaceData_resources(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type NamespaceData", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _Query_allEvents(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_allEvents(ctx, field) if err != nil { @@ -6525,7 +6482,7 @@ func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().UniqueNamespaces(rctx) + return ec.resolvers.Query().UniqueNamespaces(rctx, fc.Args["clusterName"].(string)) }) if err != nil { ec.Error(ctx, err) @@ -6556,6 +6513,17 @@ func (ec *executionContext) fieldContext_Query_uniqueNamespaces(ctx context.Cont return nil, fmt.Errorf("no field named %q was found under type Namespace", field.Name) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_uniqueNamespaces_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } @@ -12631,28 +12599,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Query") - case "allNamespaceData": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._Query_allNamespaceData(ctx, field) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - rrm := func(ctx context.Context) graphql.Marshaler { - return ec.OperationContext.RootResolverMiddleware(ctx, - func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "allEvents": field := field @@ -14443,60 +14389,6 @@ func (ec *executionContext) marshalNNamespace2ᚖgithubᚗcomᚋintelopsᚋkubvi return ec._Namespace(ctx, sel, v) } -func (ec *executionContext) marshalNNamespaceData2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceDataᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.NamespaceData) graphql.Marshaler { - ret := make(graphql.Array, len(v)) - var wg sync.WaitGroup - isLen1 := len(v) == 1 - if !isLen1 { - wg.Add(len(v)) - } - for i := range v { - i := i - fc := &graphql.FieldContext{ - Index: &i, - Result: &v[i], - } - ctx := graphql.WithFieldContext(ctx, fc) - f := func(i int) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = nil - } - }() - if !isLen1 { - defer wg.Done() - } - ret[i] = ec.marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx, sel, v[i]) - } - if isLen1 { - f(i) - } else { - go f(i) - } - - } - wg.Wait() - - for _, e := range ret { - if e == graphql.Null { - return graphql.Null - } - } - - return ret -} - -func (ec *executionContext) marshalNNamespaceData2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceData(ctx context.Context, sel ast.SelectionSet, v *model.NamespaceData) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._NamespaceData(ctx, sel, v) -} - func (ec *executionContext) marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.OutdatedImage) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index f96ea03b..cd5978a6 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -1,5 +1,4 @@ type Query { - allNamespaceData: [NamespaceData!]! allEvents: [Event!]! allRakkess: [Rakkess!]! allDeprecatedAPIs: [DeprecatedAPI!]! @@ -10,7 +9,7 @@ type Query { allKubeScores: [Kubescore!]! allTrivyVuls: [TrivyVul!]! allTrivyMisconfigs: [TrivyMisconfig!]! - uniqueNamespaces: [Namespace!]! + uniqueNamespaces(clusterName: String!): [Namespace!]! uniqueClusters: [Cluster!]! outdatedImagesByClusterAndNamespace(clusterName: String!, namespace: String!): [OutdatedImage!]! outdatedImagesCount(clusterName: String!, namespace: String!): Int! diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index 49e2d694..40c6e177 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -12,47 +12,6 @@ import ( "github.com/intelops/kubviz/graphqlserver/graph/model" ) -// AllNamespaceData is the resolver for the allNamespaceData field. -func (r *queryResolver) AllNamespaceData(ctx context.Context) ([]*model.NamespaceData, error) { - var namespaceDataList []*model.NamespaceData - - namespaces, err := r.fetchNamespacesFromDatabase(ctx) - if err != nil { - return nil, fmt.Errorf("error fetching namespaces: %v", err) - } - - for _, ns := range namespaces { - outdatedImages, err := r.fetchOutdatedImages(ctx, ns) - if err != nil { - fmt.Printf("error fetching outdated images for namespace %s: %v\n", ns, err) - outdatedImages = []*model.OutdatedImage{} - } - - kubeScores, err := r.fetchKubeScores(ctx, ns) - if err != nil { - fmt.Printf("error fetching kube scores for namespace %s: %v\n", ns, err) - kubeScores = []*model.KubeScore{} - } - - resources, err := r.fetchResources(ctx, ns) - if err != nil { - fmt.Printf("error fetching resources for namespace %s: %v\n", ns, err) - resources = []*model.Resource{} - } - - nd := &model.NamespaceData{ - Namespace: ns, - OutdatedImages: outdatedImages, - KubeScores: kubeScores, - Resources: resources, - } - - namespaceDataList = append(namespaceDataList, nd) - } - - return namespaceDataList, nil -} - // AllEvents is the resolver for the allEvents field. func (r *queryResolver) AllEvents(ctx context.Context) ([]*model.Event, error) { if r.DB == nil { @@ -396,8 +355,8 @@ func (r *queryResolver) AllTrivyMisconfigs(ctx context.Context) ([]*model.TrivyM } // UniqueNamespaces is the resolver for the uniqueNamespaces field. -func (r *queryResolver) UniqueNamespaces(ctx context.Context) ([]*model.Namespace, error) { - namespaces, err := r.fetchNamespacesFromDatabase(ctx) +func (r *queryResolver) UniqueNamespaces(ctx context.Context, clusterName string) ([]*model.Namespace, error) { + namespaces, err := r.fetchNamespacesFromDatabase(ctx, clusterName) if err != nil { return nil, err } diff --git a/graphqlserver/graph/utils.go b/graphqlserver/graph/utils.go index 01ad5c2d..dd7bb02a 100644 --- a/graphqlserver/graph/utils.go +++ b/graphqlserver/graph/utils.go @@ -36,13 +36,14 @@ func (r *Resolver) fetchClustersFromDatabase(ctx context.Context) ([]string, err return clusters, nil } -func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context) ([]string, error) { +func (r *Resolver) fetchNamespacesFromDatabase(ctx context.Context, clusterName string) ([]string, error) { if r.DB == nil { return nil, fmt.Errorf("database connection is not initialized") } - query := `SELECT DISTINCT Namespace FROM events` + // Include the cluster name in the WHERE clause to filter namespaces by cluster + query := `SELECT DISTINCT Namespace FROM events WHERE ClusterName = ?` - rows, err := r.DB.QueryContext(ctx, query) + rows, err := r.DB.QueryContext(ctx, query, clusterName) if err != nil { return nil, fmt.Errorf("error executing query: %v", err) } From db25414fa563b123ac753d9ad3b7b87dea5ca0c7 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 20 Feb 2024 11:22:07 +0530 Subject: [PATCH 225/263] img --- .../plugins/events/event_metrics_utils.go | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/agent/kubviz/plugins/events/event_metrics_utils.go b/agent/kubviz/plugins/events/event_metrics_utils.go index 15b2a8f2..17ef114f 100644 --- a/agent/kubviz/plugins/events/event_metrics_utils.go +++ b/agent/kubviz/plugins/events/event_metrics_utils.go @@ -3,6 +3,7 @@ package events import ( "context" "encoding/json" + "errors" "fmt" "log" "os" @@ -40,7 +41,7 @@ func PublishMetrics(clientset *kubernetes.Clientset, js nats.JetStreamContext, e errCh <- nil } -func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext) (bool, error) { +func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStreamContext, imageName string) (bool, error) { ctx := context.Background() tracer := otel.Tracer("kubviz-publish-k8smetrics") @@ -53,6 +54,7 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre Type: mtype, Event: mdata, ClusterName: ClusterName, + ImageName: imageName, } metricsJson, _ := json.Marshal(metrics) _, err := js.Publish(constants.EventSubject, metricsJson) @@ -63,6 +65,27 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre return false, nil } +func getK8sPodImages(clientset *kubernetes.Clientset, namespace, podName string) ([]string, error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + pod, err := clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + var images []string + for _, container := range pod.Spec.Containers { + images = append(images, container.Image) + } + + if len(images) == 0 { + return nil, errors.New("no containers found in the pod") + } + + return images, nil +} + // createStream creates a stream by using JetStreamContext func CreateStream(js nats.JetStreamContext) error { // Check if the METRICS stream already exists; if not, create it. @@ -162,15 +185,36 @@ func watchK8sEvents(clientset *kubernetes.Clientset, js nats.JetStreamContext) { cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { event := obj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js) + images, err := getK8sPodImages(clientset, event.InvolvedObject.Namespace, event.InvolvedObject.Name) + if err != nil { + log.Println("Error retrieving image names:", err) + return + } + for _, image := range images { + publishK8sMetrics(string(event.ObjectMeta.UID), "ADD", event, js, image) + } }, DeleteFunc: func(obj interface{}) { event := obj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js) + images, err := getK8sPodImages(clientset, event.InvolvedObject.Namespace, event.InvolvedObject.Name) + if err != nil { + log.Println("Error retrieving image names:", err) + return + } + for _, image := range images { + publishK8sMetrics(string(event.ObjectMeta.UID), "DELETE", event, js, image) + } }, UpdateFunc: func(oldObj, newObj interface{}) { event := newObj.(*v1.Event) - publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js) + images, err := getK8sPodImages(clientset, event.InvolvedObject.Namespace, event.InvolvedObject.Name) + if err != nil { + log.Println("Error retrieving image names:", err) + return + } + for _, image := range images { + publishK8sMetrics(string(event.ObjectMeta.UID), "UPDATE", event, js, image) + } }, }, ) From 82eb5aadceb70f14002f173219e85c1e05e3a90f Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 20 Feb 2024 11:38:44 +0530 Subject: [PATCH 226/263] chart-change --- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 35de376e..694dc262 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.13 +version: 1.1.14 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index 5321f029..db0c1603 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -62,7 +62,7 @@ spec: key: {{ .Values.nats.auth.secret.key }} {{- end }} - name: KUBERHEALTHY_ENABLE - value: {{ .Values.kuberhealthy.enabled }} + value: "{{ .Values.kuberhealthy.enabled }}" - name: NATS_ADDRESS value: {{ .Values.nats.host }} - name: SCHEDULING_INTERVAL From 32f4d56c04125fcab8a51d4834c0a9edb275bb64 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Wed, 28 Feb 2024 10:10:01 +0530 Subject: [PATCH 227/263] trivy changes --- .../plugins/events/event_metrics_utils.go | 2 +- .../plugins/kubepreupgrade/kubePreUpgrade.go | 2 +- agent/kubviz/plugins/kubescore/kube_score.go | 2 +- agent/kubviz/plugins/outdated/outdated.go | 2 +- agent/kubviz/plugins/rakkess/rakees_agent.go | 3 +- agent/kubviz/plugins/trivy/trivy_image.go | 99 ++++++++++++++++++- 6 files changed, 99 insertions(+), 11 deletions(-) diff --git a/agent/kubviz/plugins/events/event_metrics_utils.go b/agent/kubviz/plugins/events/event_metrics_utils.go index 17ef114f..62db9cf5 100644 --- a/agent/kubviz/plugins/events/event_metrics_utils.go +++ b/agent/kubviz/plugins/events/event_metrics_utils.go @@ -61,7 +61,7 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre if err != nil { return true, err } - log.Printf("Metrics with ID:%s has been published\n", id) + //log.Printf("Metrics with ID:%s has been published\n", id) return false, nil } diff --git a/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go b/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go index 4fb00b35..6b07ea24 100644 --- a/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go +++ b/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go @@ -79,7 +79,7 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon } } - log.Printf("Metrics with Deletedapi and depricated api has been published") + //log.Printf("Metrics with Deletedapi and depricated api has been published") return nil } diff --git a/agent/kubviz/plugins/kubescore/kube_score.go b/agent/kubviz/plugins/kubescore/kube_score.go index 660aa175..be1bdb82 100644 --- a/agent/kubviz/plugins/kubescore/kube_score.go +++ b/agent/kubviz/plugins/kubescore/kube_score.go @@ -81,7 +81,7 @@ func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamCon return err } //log.Printf("Recommendations with ID:%s has been published\n", id) - log.Printf("Recommendations :%#v", report) + //log.Printf("Recommendations :%#v", report) return nil } diff --git a/agent/kubviz/plugins/outdated/outdated.go b/agent/kubviz/plugins/outdated/outdated.go index 975c510f..8415a44c 100644 --- a/agent/kubviz/plugins/outdated/outdated.go +++ b/agent/kubviz/plugins/outdated/outdated.go @@ -75,7 +75,7 @@ func PublishOutdatedImages(out model.CheckResultfinal, js nats.JetStreamContext) if err != nil { return err } - log.Printf("Metrics with outdated images has been published") + //log.Printf("Metrics with outdated images has been published") return nil } diff --git a/agent/kubviz/plugins/rakkess/rakees_agent.go b/agent/kubviz/plugins/rakkess/rakees_agent.go index 93414db3..d453614b 100644 --- a/agent/kubviz/plugins/rakkess/rakees_agent.go +++ b/agent/kubviz/plugins/rakkess/rakees_agent.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "log" "os" "os/signal" "syscall" @@ -100,7 +99,7 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { if err != nil { return err } - log.Printf("Metrics with resource %s has been published", resourceType) + //log.Printf("Metrics with resource %s has been published", resourceType) } return nil diff --git a/agent/kubviz/plugins/trivy/trivy_image.go b/agent/kubviz/plugins/trivy/trivy_image.go index c0acff33..c4919a18 100644 --- a/agent/kubviz/plugins/trivy/trivy_image.go +++ b/agent/kubviz/plugins/trivy/trivy_image.go @@ -1,23 +1,26 @@ package trivy import ( + "bytes" "context" "encoding/json" "fmt" "log" "os" + exec "os/exec" "strings" "github.com/aquasecurity/trivy/pkg/types" "github.com/google/uuid" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" - "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "github.com/pkg/errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) @@ -37,7 +40,7 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { span.SetAttributes(attribute.String("trivy-image-scan-agent", "image-scan")) defer span.End() - images, err := outdated.ListImages(config) + images, err := ListImages(config) if err != nil { log.Println("error occured while trying to list images, error :", err.Error()) return err @@ -46,13 +49,13 @@ func RunTrivyImageScans(config *rest.Config, js nats.JetStreamContext) error { for _, image := range images { var report types.Report scanCmd := fmt.Sprintf("trivy image %s --timeout 60m -f json -q --cache-dir %s", image.PullableImage, trivyImageCacheDir) - out, err := kubescore.ExecuteCommand(scanCmd) + out, err := executeTrivyImage(scanCmd) if err != nil { log.Printf("Error scanning image %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error } - parts := strings.SplitN(out, "{", 2) + parts := strings.SplitN(string(out), "{", 2) if len(parts) <= 1 { log.Println("No output from image scan command", err) continue // Move on to the next image if there's no output @@ -95,3 +98,89 @@ func PublishImageScanReports(report types.Report, js nats.JetStreamContext) erro log.Printf("Trivy image report with ID:%s has been published\n", metrics.ID) return nil } +func executeTrivyImage(command string) ([]byte, error) { + + // ctx := context.Background() + // tracer := otel.Tracer("trivy-image") + // _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandTrivyImage") + // span.SetAttributes(attribute.String("trivy-image-agent", "trivyimage-command-running")) + // defer span.End() + + cmd := exec.Command("/bin/sh", "-c", command) + var outc, errc bytes.Buffer + cmd.Stdout = &outc + cmd.Stderr = &errc + err := cmd.Run() + // if outc.Len() > 0 { + // log.Printf("Command Output: %s\n", outc.String()) + // } + if errc.Len() > 0 { + log.Printf("Command Error: %s\n", errc.String()) + } + if err != nil { + return nil, fmt.Errorf("error while executing trivy image command: %v", err) + } + return outc.Bytes(), err +} + +func ListImages(config *rest.Config) ([]model.RunningImage, error) { + var err error + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, errors.Wrap(err, "failed to create clientset") + } + ctx := context.Background() + namespaces, err := clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, errors.Wrap(err, "failed to list namespaces") + } + + runningImages := []model.RunningImage{} + for _, namespace := range namespaces.Items { + pods, err := clientset.CoreV1().Pods(namespace.Name).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, errors.Wrap(err, "failed to list pods") + } + + for _, pod := range pods.Items { + for _, initContainerStatus := range pod.Status.InitContainerStatuses { + pullable := initContainerStatus.ImageID + pullable = strings.TrimPrefix(pullable, "docker-pullable://") + runningImage := model.RunningImage{ + Pod: pod.Name, + Namespace: pod.Namespace, + InitContainer: &initContainerStatus.Name, + Image: initContainerStatus.Image, + PullableImage: pullable, + } + runningImages = append(runningImages, runningImage) + } + + for _, containerStatus := range pod.Status.ContainerStatuses { + pullable := containerStatus.ImageID + pullable = strings.TrimPrefix(pullable, "docker-pullable://") + + runningImage := model.RunningImage{ + Pod: pod.Name, + Namespace: pod.Namespace, + Container: &containerStatus.Name, + Image: containerStatus.Image, + PullableImage: pullable, + } + runningImages = append(runningImages, runningImage) + } + } + } + + // Remove exact duplicates + cleanedImages := []model.RunningImage{} + seenImages := make(map[string]bool) + for _, runningImage := range runningImages { + if !seenImages[runningImage.PullableImage] { + cleanedImages = append(cleanedImages, runningImage) + seenImages[runningImage.PullableImage] = true + } + } + + return cleanedImages, nil +} From d46ae02a969a869fa6a9372345c36ffe593f76a0 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Wed, 28 Feb 2024 10:53:12 +0530 Subject: [PATCH 228/263] trivy changes --- agent/kubviz/k8smetrics_agent.go | 136 ++++++++++++++++--------------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 5617a21f..5475947f 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -3,11 +3,12 @@ package main import ( "log" "os" - "os/signal" - "syscall" + + //"os/signal" + //"syscall" "time" - "github.com/go-co-op/gocron" + //"github.com/go-co-op/gocron" "github.com/nats-io/nats.go" "context" @@ -15,28 +16,29 @@ import ( "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/intelops/kubviz/pkg/opentelemetry" - "k8s.io/client-go/kubernetes" + //"k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - "github.com/intelops/kubviz/agent/config" + //"github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/agent/kubviz/plugins/events" - "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" - "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" - "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" - "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" - "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + //"github.com/intelops/kubviz/agent/kubviz/plugins/ketall" + //"github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + + //"github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" + //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + //"github.com/intelops/kubviz/agent/kubviz/plugins/outdated" + //"github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" - "github.com/intelops/kubviz/agent/kubviz/scheduler" + //"github.com/intelops/kubviz/agent/kubviz/scheduler" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" - "github.com/intelops/kubviz/agent/server" + //"github.com/intelops/kubviz/agent/server" "k8s.io/client-go/tools/clientcmd" ) @@ -64,14 +66,14 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - clusterMetricsChan := make(chan error, 1) - cfg, err := config.GetAgentConfigurations() - if err != nil { - log.Fatal("Failed to retrieve agent configurations", err) - } + // clusterMetricsChan := make(chan error, 1) + // cfg, err := config.GetAgentConfigurations() + // if err != nil { + // log.Fatal("Failed to retrieve agent configurations", err) + // } var ( - config *rest.Config - clientset *kubernetes.Clientset + config *rest.Config + //clientset *kubernetes.Clientset ) var mtlsConfig mtlsnats.MtlsConfig @@ -97,8 +99,8 @@ func main() { } if nc == nil { - nc, err = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - events.CheckErr(err) + nc, _ = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + //events.CheckErr(err) } js, err := nc.JetStream() events.CheckErr(err) @@ -109,13 +111,13 @@ func main() { if err != nil { log.Fatal(err) } - clientset = events.GetK8sClient(config) + //clientset = events.GetK8sClient(config) } else { config, err = rest.InClusterConfig() if err != nil { log.Fatal(err) } - clientset = events.GetK8sClient(config) + //clientset = events.GetK8sClient(config) } tp, err := opentelemetry.InitTracer() @@ -128,54 +130,54 @@ func main() { } }() - go events.PublishMetrics(clientset, js, clusterMetricsChan) - if cfg.KuberHealthyEnable { - go kuberhealthy.StartKuberHealthy(js) - } - go server.StartServer() + // go events.PublishMetrics(clientset, js, clusterMetricsChan) + // if cfg.KuberHealthyEnable { + // go kuberhealthy.StartKuberHealthy(js) + // } + // go server.StartServer() collectAndPublishMetrics := func() { - err := outdated.OutDatedImages(config, js) - events.LogErr(err) - err = kubepreupgrade.KubePreUpgradeDetector(config, js) - events.LogErr(err) - err = ketall.GetAllResources(config, js) - events.LogErr(err) - err = rakkess.RakeesOutput(config, js) - events.LogErr(err) - err = trivy.RunTrivySbomScan(config, js) - events.LogErr(err) + // err := outdated.OutDatedImages(config, js) + // events.LogErr(err) + // err = kubepreupgrade.KubePreUpgradeDetector(config, js) + // events.LogErr(err) + // err = ketall.GetAllResources(config, js) + // events.LogErr(err) + // err = rakkess.RakeesOutput(config, js) + // events.LogErr(err) + // err = trivy.RunTrivySbomScan(config, js) + // events.LogErr(err) err = trivy.RunTrivyImageScans(config, js) events.LogErr(err) - err = trivy.RunTrivyK8sClusterScan(js) - events.LogErr(err) - err = kubescore.RunKubeScore(clientset, js) - events.LogErr(err) + // err = trivy.RunTrivyK8sClusterScan(js) + // events.LogErr(err) + // err = kubescore.RunKubeScore(clientset, js) + // events.LogErr(err) } collectAndPublishMetrics() - if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. - scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) - - // Start the scheduler - scheduler.Start() - signals := make(chan os.Signal, 1) - signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - <-signals - - scheduler.Stop() - } else { - if schedulingIntervalStr == "" { - schedulingIntervalStr = "20m" - } - schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) - if err != nil { - log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) - } - s := gocron.NewScheduler(time.UTC) - s.Every(schedulingInterval).Do(func() { - collectAndPublishMetrics() - }) - s.StartBlocking() - } + // if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. + // scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) + + // // Start the scheduler + // scheduler.Start() + // signals := make(chan os.Signal, 1) + // signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + // <-signals + + // scheduler.Stop() + // } else { + // if schedulingIntervalStr == "" { + // schedulingIntervalStr = "20m" + // } + // schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) + // if err != nil { + // log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) + // } + // s := gocron.NewScheduler(time.UTC) + // s.Every(schedulingInterval).Do(func() { + // collectAndPublishMetrics() + // }) + // s.StartBlocking() + // } } From 60e559e1d87d3c857613dfc1a6cded3f60cbe938 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Wed, 28 Feb 2024 11:13:18 +0530 Subject: [PATCH 229/263] trivy changes --- agent/kubviz/k8smetrics_agent.go | 133 +++++++++--------- .../plugins/events/event_metrics_utils.go | 2 +- .../plugins/kubepreupgrade/kubePreUpgrade.go | 2 +- agent/kubviz/plugins/kubescore/kube_score.go | 2 +- agent/kubviz/plugins/outdated/outdated.go | 2 +- agent/kubviz/plugins/rakkess/rakees_agent.go | 3 +- 6 files changed, 73 insertions(+), 71 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 5475947f..8fd89963 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -4,11 +4,12 @@ import ( "log" "os" - //"os/signal" - //"syscall" + "os/signal" + "syscall" "time" //"github.com/go-co-op/gocron" + "github.com/go-co-op/gocron" "github.com/nats-io/nats.go" "context" @@ -16,29 +17,29 @@ import ( "github.com/intelops/kubviz/pkg/mtlsnats" "github.com/intelops/kubviz/pkg/opentelemetry" - //"k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - //"github.com/intelops/kubviz/agent/config" + "github.com/intelops/kubviz/agent/config" "github.com/intelops/kubviz/agent/kubviz/plugins/events" - //"github.com/intelops/kubviz/agent/kubviz/plugins/ketall" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" + "github.com/intelops/kubviz/agent/kubviz/plugins/ketall" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubepreupgrade" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" - //"github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" - //"github.com/intelops/kubviz/agent/kubviz/plugins/outdated" - //"github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" + "github.com/intelops/kubviz/agent/kubviz/plugins/kuberhealthy" + "github.com/intelops/kubviz/agent/kubviz/plugins/kubescore" + "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" + "github.com/intelops/kubviz/agent/kubviz/plugins/rakkess" "github.com/intelops/kubviz/agent/kubviz/plugins/trivy" - //"github.com/intelops/kubviz/agent/kubviz/scheduler" + "github.com/intelops/kubviz/agent/kubviz/scheduler" _ "k8s.io/client-go/plugin/pkg/client/auth/azure" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" - // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" - //"github.com/intelops/kubviz/agent/server" + "github.com/intelops/kubviz/agent/server" + //_ "k8s.io/client-go/plugin/pkg/client/auth/openstack" "k8s.io/client-go/tools/clientcmd" ) @@ -66,14 +67,14 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - // clusterMetricsChan := make(chan error, 1) - // cfg, err := config.GetAgentConfigurations() - // if err != nil { - // log.Fatal("Failed to retrieve agent configurations", err) - // } + clusterMetricsChan := make(chan error, 1) + cfg, err := config.GetAgentConfigurations() + if err != nil { + log.Fatal("Failed to retrieve agent configurations", err) + } var ( - config *rest.Config - //clientset *kubernetes.Clientset + config *rest.Config + clientset *kubernetes.Clientset ) var mtlsConfig mtlsnats.MtlsConfig @@ -100,7 +101,7 @@ func main() { if nc == nil { nc, _ = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) - //events.CheckErr(err) + events.CheckErr(err) } js, err := nc.JetStream() events.CheckErr(err) @@ -111,13 +112,13 @@ func main() { if err != nil { log.Fatal(err) } - //clientset = events.GetK8sClient(config) + clientset = events.GetK8sClient(config) } else { config, err = rest.InClusterConfig() if err != nil { log.Fatal(err) } - //clientset = events.GetK8sClient(config) + clientset = events.GetK8sClient(config) } tp, err := opentelemetry.InitTracer() @@ -130,54 +131,54 @@ func main() { } }() - // go events.PublishMetrics(clientset, js, clusterMetricsChan) - // if cfg.KuberHealthyEnable { - // go kuberhealthy.StartKuberHealthy(js) - // } - // go server.StartServer() + go events.PublishMetrics(clientset, js, clusterMetricsChan) + if cfg.KuberHealthyEnable { + go kuberhealthy.StartKuberHealthy(js) + } + go server.StartServer() collectAndPublishMetrics := func() { - // err := outdated.OutDatedImages(config, js) - // events.LogErr(err) - // err = kubepreupgrade.KubePreUpgradeDetector(config, js) - // events.LogErr(err) - // err = ketall.GetAllResources(config, js) - // events.LogErr(err) - // err = rakkess.RakeesOutput(config, js) - // events.LogErr(err) - // err = trivy.RunTrivySbomScan(config, js) - // events.LogErr(err) + err := outdated.OutDatedImages(config, js) + events.LogErr(err) + err = kubepreupgrade.KubePreUpgradeDetector(config, js) + events.LogErr(err) + err = ketall.GetAllResources(config, js) + events.LogErr(err) + err = rakkess.RakeesOutput(config, js) + events.LogErr(err) + err = trivy.RunTrivySbomScan(config, js) + events.LogErr(err) err = trivy.RunTrivyImageScans(config, js) events.LogErr(err) - // err = trivy.RunTrivyK8sClusterScan(js) - // events.LogErr(err) - // err = kubescore.RunKubeScore(clientset, js) - // events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(js) + events.LogErr(err) + err = kubescore.RunKubeScore(clientset, js) + events.LogErr(err) } collectAndPublishMetrics() - // if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. - // scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) - - // // Start the scheduler - // scheduler.Start() - // signals := make(chan os.Signal, 1) - // signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - // <-signals - - // scheduler.Stop() - // } else { - // if schedulingIntervalStr == "" { - // schedulingIntervalStr = "20m" - // } - // schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) - // if err != nil { - // log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) - // } - // s := gocron.NewScheduler(time.UTC) - // s.Every(schedulingInterval).Do(func() { - // collectAndPublishMetrics() - // }) - // s.StartBlocking() - // } + if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. + scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) + + // Start the scheduler + scheduler.Start() + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + <-signals + + scheduler.Stop() + } else { + if schedulingIntervalStr == "" { + schedulingIntervalStr = "20m" + } + schedulingInterval, err := time.ParseDuration(schedulingIntervalStr) + if err != nil { + log.Fatalf("Failed to parse SCHEDULING_INTERVAL: %v", err) + } + s := gocron.NewScheduler(time.UTC) + s.Every(schedulingInterval).Do(func() { + collectAndPublishMetrics() + }) + s.StartBlocking() + } } diff --git a/agent/kubviz/plugins/events/event_metrics_utils.go b/agent/kubviz/plugins/events/event_metrics_utils.go index 62db9cf5..17ef114f 100644 --- a/agent/kubviz/plugins/events/event_metrics_utils.go +++ b/agent/kubviz/plugins/events/event_metrics_utils.go @@ -61,7 +61,7 @@ func publishK8sMetrics(id string, mtype string, mdata *v1.Event, js nats.JetStre if err != nil { return true, err } - //log.Printf("Metrics with ID:%s has been published\n", id) + log.Printf("Metrics with ID:%s has been published\n", id) return false, nil } diff --git a/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go b/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go index 6b07ea24..4fb00b35 100644 --- a/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go +++ b/agent/kubviz/plugins/kubepreupgrade/kubePreUpgrade.go @@ -79,7 +79,7 @@ func publishK8sDepricated_Deleted_Api(result *model.Result, js nats.JetStreamCon } } - //log.Printf("Metrics with Deletedapi and depricated api has been published") + log.Printf("Metrics with Deletedapi and depricated api has been published") return nil } diff --git a/agent/kubviz/plugins/kubescore/kube_score.go b/agent/kubviz/plugins/kubescore/kube_score.go index be1bdb82..660aa175 100644 --- a/agent/kubviz/plugins/kubescore/kube_score.go +++ b/agent/kubviz/plugins/kubescore/kube_score.go @@ -81,7 +81,7 @@ func publishKubescoreMetrics(report []json_v2.ScoredObject, js nats.JetStreamCon return err } //log.Printf("Recommendations with ID:%s has been published\n", id) - //log.Printf("Recommendations :%#v", report) + log.Printf("Recommendations :%#v", report) return nil } diff --git a/agent/kubviz/plugins/outdated/outdated.go b/agent/kubviz/plugins/outdated/outdated.go index 8415a44c..975c510f 100644 --- a/agent/kubviz/plugins/outdated/outdated.go +++ b/agent/kubviz/plugins/outdated/outdated.go @@ -75,7 +75,7 @@ func PublishOutdatedImages(out model.CheckResultfinal, js nats.JetStreamContext) if err != nil { return err } - //log.Printf("Metrics with outdated images has been published") + log.Printf("Metrics with outdated images has been published") return nil } diff --git a/agent/kubviz/plugins/rakkess/rakees_agent.go b/agent/kubviz/plugins/rakkess/rakees_agent.go index d453614b..93414db3 100644 --- a/agent/kubviz/plugins/rakkess/rakees_agent.go +++ b/agent/kubviz/plugins/rakkess/rakees_agent.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log" "os" "os/signal" "syscall" @@ -99,7 +100,7 @@ func RakeesOutput(config *rest.Config, js nats.JetStreamContext) error { if err != nil { return err } - //log.Printf("Metrics with resource %s has been published", resourceType) + log.Printf("Metrics with resource %s has been published", resourceType) } return nil From 2e5eb833741d433186fb192a73df6e410e927a81 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Wed, 28 Feb 2024 11:14:50 +0530 Subject: [PATCH 230/263] trivy changes --- agent/kubviz/k8smetrics_agent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 8fd89963..068360eb 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -100,7 +100,7 @@ func main() { } if nc == nil { - nc, _ = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) + nc, err = nats.Connect(natsurl, nats.Name("K8s Metrics"), nats.Token(token)) events.CheckErr(err) } js, err := nc.JetStream() From 67c38e006b2ef11446c754ed20ef97f8713177f2 Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 1 Mar 2024 11:12:44 +0530 Subject: [PATCH 231/263] added new graphql handlers --- .gitignore | 1 + client/pkg/config/config.go | 7 + graphqlserver/graph/generated.go | 7957 ++++++++++++++++++----- graphqlserver/graph/model/models_gen.go | 65 +- graphqlserver/graph/schema.graphqls | 116 +- graphqlserver/graph/schema.resolvers.go | 396 +- graphqlserver/server.go | 13 +- 7 files changed, 6741 insertions(+), 1814 deletions(-) diff --git a/.gitignore b/.gitignore index 87c9216c..87460586 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ alloc.svg allocs.pprof cpu.pprof steps-to-test.txt +Dockerfile-grphqlserver-build diff --git a/client/pkg/config/config.go b/client/pkg/config/config.go index d0dceff1..94045005 100644 --- a/client/pkg/config/config.go +++ b/client/pkg/config/config.go @@ -25,3 +25,10 @@ type Config struct { S3BucketName string `envconfig:"S3_BUCKET_NAME"` S3ObjectKey string `envconfig:"S3_OBJECT_KEY"` } + +type GraphQlConfig struct { + DbPort int `envconfig:"DB_PORT"` + DBAddress string `envconfig:"DB_ADDRESS"` + ClickHouseUsername string `envconfig:"CLICKHOUSE_USERNAME"` + ClickHousePassword string `envconfig:"CLICKHOUSE_PASSWORD"` +} diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index 7a5b07b3..8b58dd37 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -55,6 +55,17 @@ type ComplexityRoot struct { Count func(childComplexity int) int } + ClusterDeletedAPICount struct { + ClusterName func(childComplexity int) int + DeletedAPICount func(childComplexity int) int + } + + ClusterNamespaceMisconfigCount struct { + ClusterName func(childComplexity int) int + MisconfigCount func(childComplexity int) int + Namespace func(childComplexity int) int + } + ClusterNamespaceOutdatedCount struct { ClusterName func(childComplexity int) int Namespace func(childComplexity int) int @@ -67,6 +78,12 @@ type ComplexityRoot struct { ResourceCount func(childComplexity int) int } + ClusterNamespaceVulCount struct { + ClusterName func(childComplexity int) int + Namespace func(childComplexity int) int + VulCount func(childComplexity int) int + } + DeletedAPI struct { ClusterName func(childComplexity int) int Deleted func(childComplexity int) int @@ -141,7 +158,6 @@ type ComplexityRoot struct { ClusterName func(childComplexity int) int Description func(childComplexity int) int EventTime func(childComplexity int) int - ExpiryDate func(childComplexity int) int FileName func(childComplexity int) int FileRow func(childComplexity int) int ID func(childComplexity int) int @@ -154,6 +170,27 @@ type ComplexityRoot struct { TargetType func(childComplexity int) int } + Misconfiguration struct { + ClusterName func(childComplexity int) int + EventTime func(childComplexity int) int + ExpiryDate func(childComplexity int) int + ExportedAt func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + MisconfigAvdid func(childComplexity int) int + MisconfigDesc func(childComplexity int) int + MisconfigID func(childComplexity int) int + MisconfigMsg func(childComplexity int) int + MisconfigQuery func(childComplexity int) int + MisconfigResolution func(childComplexity int) int + MisconfigSeverity func(childComplexity int) int + MisconfigStatus func(childComplexity int) int + MisconfigTitle func(childComplexity int) int + MisconfigType func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + } + Namespace struct { Name func(childComplexity int) int } @@ -191,11 +228,23 @@ type ComplexityRoot struct { AllTrivyMisconfigs func(childComplexity int) int AllTrivySBOMs func(childComplexity int) int AllTrivyVuls func(childComplexity int) int + DeletedAPICount func(childComplexity int, clusterName string) int + DeletedAPIs func(childComplexity int, clusterName string) int + DeprecatedAPIs func(childComplexity int, clusterName string) int EventsByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int + GetAllResources func(childComplexity int, clusterName string, namespace string) int + Kubescores func(childComplexity int, clustername string, namespace string) int + Misconfigurations func(childComplexity int, clusterName string, namespace string) int OutdatedImagesByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int OutdatedImagesCount func(childComplexity int, clusterName string, namespace string) int + TrivyImageCount func(childComplexity int, clusterName string) int + TrivyImages func(childComplexity int, clusterName string) int + TrivyMisconfigCount func(childComplexity int, clusterName string, namespace string) int + TrivySBOMs func(childComplexity int, clusterName string) int + TrivyVulCount func(childComplexity int, clusterName string, namespace string) int UniqueClusters func(childComplexity int) int UniqueNamespaces func(childComplexity int, clusterName string) int + Vulnerabilities func(childComplexity int, clusterName string, namespace string) int } Rakkess struct { @@ -234,6 +283,11 @@ type ComplexityRoot struct { VulTitle func(childComplexity int) int } + TrivyImageCount struct { + ClusterName func(childComplexity int) int + ImageCount func(childComplexity int) int + } + TrivyMisconfig struct { ClusterName func(childComplexity int) int EventTime func(childComplexity int) int @@ -286,6 +340,27 @@ type ComplexityRoot struct { VulTitle func(childComplexity int) int VulVendorIds func(childComplexity int) int } + + Vulnerability struct { + ClusterName func(childComplexity int) int + ExpiryDate func(childComplexity int) int + ExportedAt func(childComplexity int) int + ID func(childComplexity int) int + Kind func(childComplexity int) int + Name func(childComplexity int) int + Namespace func(childComplexity int) int + VulFixedVersion func(childComplexity int) int + VulID func(childComplexity int) int + VulInstalledVersion func(childComplexity int) int + VulLastModifiedDate func(childComplexity int) int + VulPkgID func(childComplexity int) int + VulPkgName func(childComplexity int) int + VulPkgPath func(childComplexity int) int + VulPublishedDate func(childComplexity int) int + VulSeverity func(childComplexity int) int + VulTitle func(childComplexity int) int + VulVendorIds func(childComplexity int) int + } } type QueryResolver interface { @@ -308,6 +383,18 @@ type QueryResolver interface { AllClusterDeletedAPIsCounts(ctx context.Context) ([]*model.ClusterAPIsCount, error) AllClusterNamespaceResourceCounts(ctx context.Context) ([]*model.ClusterNamespaceResourceCount, error) EventsByClusterAndNamespace(ctx context.Context, clusterName string, namespace string) ([]*model.Event, error) + Vulnerabilities(ctx context.Context, clusterName string, namespace string) ([]*model.Vulnerability, error) + Misconfigurations(ctx context.Context, clusterName string, namespace string) ([]*model.Misconfiguration, error) + Kubescores(ctx context.Context, clustername string, namespace string) ([]*model.KubeScore, error) + GetAllResources(ctx context.Context, clusterName string, namespace string) ([]*model.GetAllResource, error) + TrivyImages(ctx context.Context, clusterName string) ([]*model.TrivyImage, error) + DeprecatedAPIs(ctx context.Context, clusterName string) ([]*model.DeprecatedAPI, error) + DeletedAPIs(ctx context.Context, clusterName string) ([]*model.DeletedAPI, error) + TrivySBOMs(ctx context.Context, clusterName string) ([]*model.TrivySbom, error) + TrivyVulCount(ctx context.Context, clusterName string, namespace string) (*model.ClusterNamespaceVulCount, error) + TrivyMisconfigCount(ctx context.Context, clusterName string, namespace string) (*model.ClusterNamespaceMisconfigCount, error) + DeletedAPICount(ctx context.Context, clusterName string) (*model.ClusterDeletedAPICount, error) + TrivyImageCount(ctx context.Context, clusterName string) (*model.TrivyImageCount, error) } type executableSchema struct { @@ -350,6 +437,41 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ClusterAPIsCount.Count(childComplexity), true + case "ClusterDeletedAPICount.clusterName": + if e.complexity.ClusterDeletedAPICount.ClusterName == nil { + break + } + + return e.complexity.ClusterDeletedAPICount.ClusterName(childComplexity), true + + case "ClusterDeletedAPICount.deletedAPICount": + if e.complexity.ClusterDeletedAPICount.DeletedAPICount == nil { + break + } + + return e.complexity.ClusterDeletedAPICount.DeletedAPICount(childComplexity), true + + case "ClusterNamespaceMisconfigCount.clusterName": + if e.complexity.ClusterNamespaceMisconfigCount.ClusterName == nil { + break + } + + return e.complexity.ClusterNamespaceMisconfigCount.ClusterName(childComplexity), true + + case "ClusterNamespaceMisconfigCount.misconfigCount": + if e.complexity.ClusterNamespaceMisconfigCount.MisconfigCount == nil { + break + } + + return e.complexity.ClusterNamespaceMisconfigCount.MisconfigCount(childComplexity), true + + case "ClusterNamespaceMisconfigCount.namespace": + if e.complexity.ClusterNamespaceMisconfigCount.Namespace == nil { + break + } + + return e.complexity.ClusterNamespaceMisconfigCount.Namespace(childComplexity), true + case "ClusterNamespaceOutdatedCount.clusterName": if e.complexity.ClusterNamespaceOutdatedCount.ClusterName == nil { break @@ -392,6 +514,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ClusterNamespaceResourceCount.ResourceCount(childComplexity), true + case "ClusterNamespaceVulCount.clusterName": + if e.complexity.ClusterNamespaceVulCount.ClusterName == nil { + break + } + + return e.complexity.ClusterNamespaceVulCount.ClusterName(childComplexity), true + + case "ClusterNamespaceVulCount.namespace": + if e.complexity.ClusterNamespaceVulCount.Namespace == nil { + break + } + + return e.complexity.ClusterNamespaceVulCount.Namespace(childComplexity), true + + case "ClusterNamespaceVulCount.vulCount": + if e.complexity.ClusterNamespaceVulCount.VulCount == nil { + break + } + + return e.complexity.ClusterNamespaceVulCount.VulCount(childComplexity), true + case "DeletedAPI.ClusterName": if e.complexity.DeletedAPI.ClusterName == nil { break @@ -798,13 +941,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Kubescore.EventTime(childComplexity), true - case "Kubescore.expiryDate": - if e.complexity.Kubescore.ExpiryDate == nil { - break - } - - return e.complexity.Kubescore.ExpiryDate(childComplexity), true - case "Kubescore.fileName": if e.complexity.Kubescore.FileName == nil { break @@ -875,6 +1011,132 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Kubescore.TargetType(childComplexity), true + case "Misconfiguration.clusterName": + if e.complexity.Misconfiguration.ClusterName == nil { + break + } + + return e.complexity.Misconfiguration.ClusterName(childComplexity), true + + case "Misconfiguration.eventTime": + if e.complexity.Misconfiguration.EventTime == nil { + break + } + + return e.complexity.Misconfiguration.EventTime(childComplexity), true + + case "Misconfiguration.expiryDate": + if e.complexity.Misconfiguration.ExpiryDate == nil { + break + } + + return e.complexity.Misconfiguration.ExpiryDate(childComplexity), true + + case "Misconfiguration.exportedAt": + if e.complexity.Misconfiguration.ExportedAt == nil { + break + } + + return e.complexity.Misconfiguration.ExportedAt(childComplexity), true + + case "Misconfiguration.id": + if e.complexity.Misconfiguration.ID == nil { + break + } + + return e.complexity.Misconfiguration.ID(childComplexity), true + + case "Misconfiguration.kind": + if e.complexity.Misconfiguration.Kind == nil { + break + } + + return e.complexity.Misconfiguration.Kind(childComplexity), true + + case "Misconfiguration.misconfigAvdid": + if e.complexity.Misconfiguration.MisconfigAvdid == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigAvdid(childComplexity), true + + case "Misconfiguration.misconfigDesc": + if e.complexity.Misconfiguration.MisconfigDesc == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigDesc(childComplexity), true + + case "Misconfiguration.misconfigId": + if e.complexity.Misconfiguration.MisconfigID == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigID(childComplexity), true + + case "Misconfiguration.misconfigMsg": + if e.complexity.Misconfiguration.MisconfigMsg == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigMsg(childComplexity), true + + case "Misconfiguration.misconfigQuery": + if e.complexity.Misconfiguration.MisconfigQuery == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigQuery(childComplexity), true + + case "Misconfiguration.misconfigResolution": + if e.complexity.Misconfiguration.MisconfigResolution == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigResolution(childComplexity), true + + case "Misconfiguration.misconfigSeverity": + if e.complexity.Misconfiguration.MisconfigSeverity == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigSeverity(childComplexity), true + + case "Misconfiguration.misconfigStatus": + if e.complexity.Misconfiguration.MisconfigStatus == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigStatus(childComplexity), true + + case "Misconfiguration.misconfigTitle": + if e.complexity.Misconfiguration.MisconfigTitle == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigTitle(childComplexity), true + + case "Misconfiguration.misconfigType": + if e.complexity.Misconfiguration.MisconfigType == nil { + break + } + + return e.complexity.Misconfiguration.MisconfigType(childComplexity), true + + case "Misconfiguration.name": + if e.complexity.Misconfiguration.Name == nil { + break + } + + return e.complexity.Misconfiguration.Name(childComplexity), true + + case "Misconfiguration.namespace": + if e.complexity.Misconfiguration.Namespace == nil { + break + } + + return e.complexity.Misconfiguration.Namespace(childComplexity), true + case "Namespace.name": if e.complexity.Namespace.Name == nil { break @@ -1064,6 +1326,42 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.AllTrivyVuls(childComplexity), true + case "Query.deletedAPICount": + if e.complexity.Query.DeletedAPICount == nil { + break + } + + args, err := ec.field_Query_deletedAPICount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.DeletedAPICount(childComplexity, args["clusterName"].(string)), true + + case "Query.deletedAPIs": + if e.complexity.Query.DeletedAPIs == nil { + break + } + + args, err := ec.field_Query_deletedAPIs_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.DeletedAPIs(childComplexity, args["clusterName"].(string)), true + + case "Query.deprecatedAPIs": + if e.complexity.Query.DeprecatedAPIs == nil { + break + } + + args, err := ec.field_Query_deprecatedAPIs_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.DeprecatedAPIs(childComplexity, args["clusterName"].(string)), true + case "Query.eventsByClusterAndNamespace": if e.complexity.Query.EventsByClusterAndNamespace == nil { break @@ -1076,6 +1374,42 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.EventsByClusterAndNamespace(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + case "Query.getAllResources": + if e.complexity.Query.GetAllResources == nil { + break + } + + args, err := ec.field_Query_getAllResources_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.GetAllResources(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + + case "Query.kubescores": + if e.complexity.Query.Kubescores == nil { + break + } + + args, err := ec.field_Query_kubescores_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Kubescores(childComplexity, args["clustername"].(string), args["namespace"].(string)), true + + case "Query.misconfigurations": + if e.complexity.Query.Misconfigurations == nil { + break + } + + args, err := ec.field_Query_misconfigurations_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Misconfigurations(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + case "Query.outdatedImagesByClusterAndNamespace": if e.complexity.Query.OutdatedImagesByClusterAndNamespace == nil { break @@ -1100,6 +1434,66 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.OutdatedImagesCount(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + case "Query.trivyImageCount": + if e.complexity.Query.TrivyImageCount == nil { + break + } + + args, err := ec.field_Query_trivyImageCount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TrivyImageCount(childComplexity, args["clusterName"].(string)), true + + case "Query.trivyImages": + if e.complexity.Query.TrivyImages == nil { + break + } + + args, err := ec.field_Query_trivyImages_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TrivyImages(childComplexity, args["clusterName"].(string)), true + + case "Query.trivyMisconfigCount": + if e.complexity.Query.TrivyMisconfigCount == nil { + break + } + + args, err := ec.field_Query_trivyMisconfigCount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TrivyMisconfigCount(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + + case "Query.trivySBOMs": + if e.complexity.Query.TrivySBOMs == nil { + break + } + + args, err := ec.field_Query_trivySBOMs_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TrivySBOMs(childComplexity, args["clusterName"].(string)), true + + case "Query.trivyVulCount": + if e.complexity.Query.TrivyVulCount == nil { + break + } + + args, err := ec.field_Query_trivyVulCount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.TrivyVulCount(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + case "Query.uniqueClusters": if e.complexity.Query.UniqueClusters == nil { break @@ -1119,6 +1513,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.UniqueNamespaces(childComplexity, args["clusterName"].(string)), true + case "Query.vulnerabilities": + if e.complexity.Query.Vulnerabilities == nil { + break + } + + args, err := ec.field_Query_vulnerabilities_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Vulnerabilities(childComplexity, args["clusterName"].(string), args["namespace"].(string)), true + case "Rakkess.ClusterName": if e.complexity.Rakkess.ClusterName == nil { break @@ -1308,6 +1714,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.TrivyImage.VulTitle(childComplexity), true + case "TrivyImageCount.clusterName": + if e.complexity.TrivyImageCount.ClusterName == nil { + break + } + + return e.complexity.TrivyImageCount.ClusterName(childComplexity), true + + case "TrivyImageCount.ImageCount": + if e.complexity.TrivyImageCount.ImageCount == nil { + break + } + + return e.complexity.TrivyImageCount.ImageCount(childComplexity), true + case "TrivyMisconfig.clusterName": if e.complexity.TrivyMisconfig.ClusterName == nil { break @@ -1616,6 +2036,132 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.TrivyVul.VulVendorIds(childComplexity), true + case "Vulnerability.clusterName": + if e.complexity.Vulnerability.ClusterName == nil { + break + } + + return e.complexity.Vulnerability.ClusterName(childComplexity), true + + case "Vulnerability.expiryDate": + if e.complexity.Vulnerability.ExpiryDate == nil { + break + } + + return e.complexity.Vulnerability.ExpiryDate(childComplexity), true + + case "Vulnerability.exportedAt": + if e.complexity.Vulnerability.ExportedAt == nil { + break + } + + return e.complexity.Vulnerability.ExportedAt(childComplexity), true + + case "Vulnerability.id": + if e.complexity.Vulnerability.ID == nil { + break + } + + return e.complexity.Vulnerability.ID(childComplexity), true + + case "Vulnerability.kind": + if e.complexity.Vulnerability.Kind == nil { + break + } + + return e.complexity.Vulnerability.Kind(childComplexity), true + + case "Vulnerability.name": + if e.complexity.Vulnerability.Name == nil { + break + } + + return e.complexity.Vulnerability.Name(childComplexity), true + + case "Vulnerability.namespace": + if e.complexity.Vulnerability.Namespace == nil { + break + } + + return e.complexity.Vulnerability.Namespace(childComplexity), true + + case "Vulnerability.vulFixedVersion": + if e.complexity.Vulnerability.VulFixedVersion == nil { + break + } + + return e.complexity.Vulnerability.VulFixedVersion(childComplexity), true + + case "Vulnerability.vulId": + if e.complexity.Vulnerability.VulID == nil { + break + } + + return e.complexity.Vulnerability.VulID(childComplexity), true + + case "Vulnerability.vulInstalledVersion": + if e.complexity.Vulnerability.VulInstalledVersion == nil { + break + } + + return e.complexity.Vulnerability.VulInstalledVersion(childComplexity), true + + case "Vulnerability.vulLastModifiedDate": + if e.complexity.Vulnerability.VulLastModifiedDate == nil { + break + } + + return e.complexity.Vulnerability.VulLastModifiedDate(childComplexity), true + + case "Vulnerability.vulPkgId": + if e.complexity.Vulnerability.VulPkgID == nil { + break + } + + return e.complexity.Vulnerability.VulPkgID(childComplexity), true + + case "Vulnerability.vulPkgName": + if e.complexity.Vulnerability.VulPkgName == nil { + break + } + + return e.complexity.Vulnerability.VulPkgName(childComplexity), true + + case "Vulnerability.vulPkgPath": + if e.complexity.Vulnerability.VulPkgPath == nil { + break + } + + return e.complexity.Vulnerability.VulPkgPath(childComplexity), true + + case "Vulnerability.vulPublishedDate": + if e.complexity.Vulnerability.VulPublishedDate == nil { + break + } + + return e.complexity.Vulnerability.VulPublishedDate(childComplexity), true + + case "Vulnerability.vulSeverity": + if e.complexity.Vulnerability.VulSeverity == nil { + break + } + + return e.complexity.Vulnerability.VulSeverity(childComplexity), true + + case "Vulnerability.vulTitle": + if e.complexity.Vulnerability.VulTitle == nil { + break + } + + return e.complexity.Vulnerability.VulTitle(childComplexity), true + + case "Vulnerability.vulVendorIds": + if e.complexity.Vulnerability.VulVendorIds == nil { + break + } + + return e.complexity.Vulnerability.VulVendorIds(childComplexity), true + } return 0, false } @@ -1739,6 +2285,51 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Query_deletedAPICount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_deletedAPIs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_deprecatedAPIs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_eventsByClusterAndNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1763,7 +2354,7 @@ func (ec *executionContext) field_Query_eventsByClusterAndNamespace_args(ctx con return args, nil } -func (ec *executionContext) field_Query_outdatedImagesByClusterAndNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_getAllResources_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 string @@ -1787,7 +2378,31 @@ func (ec *executionContext) field_Query_outdatedImagesByClusterAndNamespace_args return args, nil } -func (ec *executionContext) field_Query_outdatedImagesCount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_kubescores_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clustername"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clustername")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clustername"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 + return args, nil +} + +func (ec *executionContext) field_Query_misconfigurations_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 string @@ -1811,7 +2426,7 @@ func (ec *executionContext) field_Query_outdatedImagesCount_args(ctx context.Con return args, nil } -func (ec *executionContext) field_Query_uniqueNamespaces_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_outdatedImagesByClusterAndNamespace_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 string @@ -1823,93 +2438,214 @@ func (ec *executionContext) field_Query_uniqueNamespaces_args(ctx context.Contex } } args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 return args, nil } -func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_outdatedImagesCount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["includeDeprecated"] = arg0 + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 return args, nil } -func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_trivyImageCount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["includeDeprecated"] = arg0 + args["clusterName"] = arg0 return args, nil } -// endregion ***************************** args.gotpl ***************************** - -// region ************************** directives.gotpl ************************** - -// endregion ************************** directives.gotpl ************************** - -// region **************************** field.gotpl ***************************** - -func (ec *executionContext) _Cluster_name(ctx context.Context, field graphql.CollectedField, obj *model.Cluster) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Cluster_name(ctx, field) - if err != nil { - return graphql.Null +func (ec *executionContext) field_Query_trivyImages_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + args["clusterName"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_trivyMisconfigCount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Name, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err } - return graphql.Null } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + args["namespace"] = arg1 + return args, nil } -func (ec *executionContext) fieldContext_Cluster_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Cluster", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, +func (ec *executionContext) field_Query_trivySBOMs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } } - return fc, nil + args["clusterName"] = arg0 + return args, nil } -func (ec *executionContext) _ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) +func (ec *executionContext) field_Query_trivyVulCount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 + return args, nil +} + +func (ec *executionContext) field_Query_uniqueNamespaces_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_vulnerabilities_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + var arg1 string + if tmp, ok := rawArgs["namespace"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("namespace")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["namespace"] = arg1 + return args, nil +} + +func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _Cluster_name(ctx context.Context, field graphql.CollectedField, obj *model.Cluster) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Cluster_name(ctx, field) if err != nil { return graphql.Null } @@ -1922,7 +2658,7 @@ func (ec *executionContext) _ClusterAPIsCount_clusterName(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -1939,9 +2675,9 @@ func (ec *executionContext) _ClusterAPIsCount_clusterName(ctx context.Context, f return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Cluster_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterAPIsCount", + Object: "Cluster", Field: field, IsMethod: false, IsResolver: false, @@ -1952,8 +2688,8 @@ func (ec *executionContext) fieldContext_ClusterAPIsCount_clusterName(ctx contex return fc, nil } -func (ec *executionContext) _ClusterAPIsCount_count(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterAPIsCount_count(ctx, field) +func (ec *executionContext) _ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -1966,7 +2702,7 @@ func (ec *executionContext) _ClusterAPIsCount_count(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Count, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -1978,26 +2714,26 @@ func (ec *executionContext) _ClusterAPIsCount_count(ctx context.Context, field g } return graphql.Null } - res := resTmp.(int) + res := resTmp.(string) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterAPIsCount_count(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterAPIsCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "ClusterAPIsCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) +func (ec *executionContext) _ClusterAPIsCount_count(ctx context.Context, field graphql.CollectedField, obj *model.ClusterAPIsCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterAPIsCount_count(ctx, field) if err != nil { return graphql.Null } @@ -2010,7 +2746,7 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount_clusterName(ctx conte }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.Count, nil }) if err != nil { ec.Error(ctx, err) @@ -2022,26 +2758,26 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount_clusterName(ctx conte } return graphql.Null } - res := resTmp.(string) + res := resTmp.(int) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterAPIsCount_count(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterNamespaceOutdatedCount", + Object: "ClusterAPIsCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _ClusterNamespaceOutdatedCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) +func (ec *executionContext) _ClusterDeletedAPICount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterDeletedAPICount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterDeletedAPICount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -2054,7 +2790,7 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount_namespace(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -2071,9 +2807,9 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount_namespace(ctx context return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterDeletedAPICount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterNamespaceOutdatedCount", + Object: "ClusterDeletedAPICount", Field: field, IsMethod: false, IsResolver: false, @@ -2084,8 +2820,8 @@ func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_namespace return fc, nil } -func (ec *executionContext) _ClusterNamespaceOutdatedCount_outdatedCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) +func (ec *executionContext) _ClusterDeletedAPICount_deletedAPICount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterDeletedAPICount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterDeletedAPICount_deletedAPICount(ctx, field) if err != nil { return graphql.Null } @@ -2098,7 +2834,7 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount_outdatedCount(ctx con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.OutdatedCount, nil + return obj.DeletedAPICount, nil }) if err != nil { ec.Error(ctx, err) @@ -2115,9 +2851,9 @@ func (ec *executionContext) _ClusterNamespaceOutdatedCount_outdatedCount(ctx con return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterDeletedAPICount_deletedAPICount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterNamespaceOutdatedCount", + Object: "ClusterDeletedAPICount", Field: field, IsMethod: false, IsResolver: false, @@ -2128,8 +2864,8 @@ func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_outdatedC return fc, nil } -func (ec *executionContext) _ClusterNamespaceResourceCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterNamespaceResourceCount_clusterName(ctx, field) +func (ec *executionContext) _ClusterNamespaceMisconfigCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceMisconfigCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceMisconfigCount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -2159,9 +2895,9 @@ func (ec *executionContext) _ClusterNamespaceResourceCount_clusterName(ctx conte return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceMisconfigCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterNamespaceResourceCount", + Object: "ClusterNamespaceMisconfigCount", Field: field, IsMethod: false, IsResolver: false, @@ -2172,8 +2908,8 @@ func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_clusterNa return fc, nil } -func (ec *executionContext) _ClusterNamespaceResourceCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterNamespaceResourceCount_namespace(ctx, field) +func (ec *executionContext) _ClusterNamespaceMisconfigCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceMisconfigCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceMisconfigCount_namespace(ctx, field) if err != nil { return graphql.Null } @@ -2203,9 +2939,9 @@ func (ec *executionContext) _ClusterNamespaceResourceCount_namespace(ctx context return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceMisconfigCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterNamespaceResourceCount", + Object: "ClusterNamespaceMisconfigCount", Field: field, IsMethod: false, IsResolver: false, @@ -2216,8 +2952,8 @@ func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_namespace return fc, nil } -func (ec *executionContext) _ClusterNamespaceResourceCount_resourceCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx, field) +func (ec *executionContext) _ClusterNamespaceMisconfigCount_misconfigCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceMisconfigCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceMisconfigCount_misconfigCount(ctx, field) if err != nil { return graphql.Null } @@ -2230,7 +2966,7 @@ func (ec *executionContext) _ClusterNamespaceResourceCount_resourceCount(ctx con }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ResourceCount, nil + return obj.MisconfigCount, nil }) if err != nil { ec.Error(ctx, err) @@ -2247,9 +2983,9 @@ func (ec *executionContext) _ClusterNamespaceResourceCount_resourceCount(ctx con return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceMisconfigCount_misconfigCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "ClusterNamespaceResourceCount", + Object: "ClusterNamespaceMisconfigCount", Field: field, IsMethod: false, IsResolver: false, @@ -2260,8 +2996,8 @@ func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_resourceC return fc, nil } -func (ec *executionContext) _DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_ClusterName(ctx, field) +func (ec *executionContext) _ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -2281,16 +3017,19 @@ func (ec *executionContext) _DeletedAPI_ClusterName(ctx context.Context, field g return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceOutdatedCount", Field: field, IsMethod: false, IsResolver: false, @@ -2301,8 +3040,8 @@ func (ec *executionContext) fieldContext_DeletedAPI_ClusterName(ctx context.Cont return fc, nil } -func (ec *executionContext) _DeletedAPI_ObjectName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_ObjectName(ctx, field) +func (ec *executionContext) _ClusterNamespaceOutdatedCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) if err != nil { return graphql.Null } @@ -2315,23 +3054,26 @@ func (ec *executionContext) _DeletedAPI_ObjectName(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ObjectName, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_ObjectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceOutdatedCount", Field: field, IsMethod: false, IsResolver: false, @@ -2342,8 +3084,8 @@ func (ec *executionContext) fieldContext_DeletedAPI_ObjectName(ctx context.Conte return fc, nil } -func (ec *executionContext) _DeletedAPI_Group(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_Group(ctx, field) +func (ec *executionContext) _ClusterNamespaceOutdatedCount_outdatedCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceOutdatedCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) if err != nil { return graphql.Null } @@ -2356,35 +3098,38 @@ func (ec *executionContext) _DeletedAPI_Group(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Group, nil + return obj.OutdatedCount, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(int) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_Group(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceOutdatedCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _DeletedAPI_Kind(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_Kind(ctx, field) +func (ec *executionContext) _ClusterNamespaceResourceCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceResourceCount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -2397,23 +3142,26 @@ func (ec *executionContext) _DeletedAPI_Kind(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceResourceCount", Field: field, IsMethod: false, IsResolver: false, @@ -2424,8 +3172,8 @@ func (ec *executionContext) fieldContext_DeletedAPI_Kind(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _DeletedAPI_Version(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_Version(ctx, field) +func (ec *executionContext) _ClusterNamespaceResourceCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceResourceCount_namespace(ctx, field) if err != nil { return graphql.Null } @@ -2438,23 +3186,26 @@ func (ec *executionContext) _DeletedAPI_Version(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Version, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_Version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceResourceCount", Field: field, IsMethod: false, IsResolver: false, @@ -2465,8 +3216,8 @@ func (ec *executionContext) fieldContext_DeletedAPI_Version(ctx context.Context, return fc, nil } -func (ec *executionContext) _DeletedAPI_Name(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_Name(ctx, field) +func (ec *executionContext) _ClusterNamespaceResourceCount_resourceCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceResourceCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx, field) if err != nil { return graphql.Null } @@ -2479,35 +3230,38 @@ func (ec *executionContext) _DeletedAPI_Name(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.ResourceCount, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(int) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceResourceCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _DeletedAPI_Deleted(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_Deleted(ctx, field) +func (ec *executionContext) _ClusterNamespaceVulCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceVulCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceVulCount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -2520,35 +3274,38 @@ func (ec *executionContext) _DeletedAPI_Deleted(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Deleted, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*bool) + res := resTmp.(string) fc.Result = res - return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_Deleted(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceVulCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceVulCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _DeletedAPI_Scope(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_Scope(ctx, field) +func (ec *executionContext) _ClusterNamespaceVulCount_namespace(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceVulCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceVulCount_namespace(ctx, field) if err != nil { return graphql.Null } @@ -2561,23 +3318,26 @@ func (ec *executionContext) _DeletedAPI_Scope(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Scope, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_Scope(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceVulCount_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceVulCount", Field: field, IsMethod: false, IsResolver: false, @@ -2588,8 +3348,8 @@ func (ec *executionContext) fieldContext_DeletedAPI_Scope(ctx context.Context, f return fc, nil } -func (ec *executionContext) _DeletedAPI_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_EventTime(ctx, field) +func (ec *executionContext) _ClusterNamespaceVulCount_vulCount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceVulCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterNamespaceVulCount_vulCount(ctx, field) if err != nil { return graphql.Null } @@ -2602,35 +3362,38 @@ func (ec *executionContext) _DeletedAPI_EventTime(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.VulCount, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(int) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ClusterNamespaceVulCount_vulCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeletedAPI", + Object: "ClusterNamespaceVulCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _DeletedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) +func (ec *executionContext) _DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -2643,7 +3406,7 @@ func (ec *executionContext) _DeletedAPI_ExpiryDate(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -2657,7 +3420,7 @@ func (ec *executionContext) _DeletedAPI_ExpiryDate(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeletedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "DeletedAPI", Field: field, @@ -2670,8 +3433,8 @@ func (ec *executionContext) fieldContext_DeletedAPI_ExpiryDate(ctx context.Conte return fc, nil } -func (ec *executionContext) _DeprecatedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) +func (ec *executionContext) _DeletedAPI_ObjectName(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_ObjectName(ctx, field) if err != nil { return graphql.Null } @@ -2684,7 +3447,7 @@ func (ec *executionContext) _DeprecatedAPI_ClusterName(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.ObjectName, nil }) if err != nil { ec.Error(ctx, err) @@ -2698,9 +3461,9 @@ func (ec *executionContext) _DeprecatedAPI_ClusterName(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_ObjectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2711,8 +3474,8 @@ func (ec *executionContext) fieldContext_DeprecatedAPI_ClusterName(ctx context.C return fc, nil } -func (ec *executionContext) _DeprecatedAPI_ObjectName(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) +func (ec *executionContext) _DeletedAPI_Group(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Group(ctx, field) if err != nil { return graphql.Null } @@ -2725,7 +3488,7 @@ func (ec *executionContext) _DeprecatedAPI_ObjectName(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ObjectName, nil + return obj.Group, nil }) if err != nil { ec.Error(ctx, err) @@ -2739,9 +3502,9 @@ func (ec *executionContext) _DeprecatedAPI_ObjectName(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_ObjectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Group(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2752,8 +3515,8 @@ func (ec *executionContext) fieldContext_DeprecatedAPI_ObjectName(ctx context.Co return fc, nil } -func (ec *executionContext) _DeprecatedAPI_Description(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_Description(ctx, field) +func (ec *executionContext) _DeletedAPI_Kind(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Kind(ctx, field) if err != nil { return graphql.Null } @@ -2766,7 +3529,7 @@ func (ec *executionContext) _DeprecatedAPI_Description(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -2780,9 +3543,9 @@ func (ec *executionContext) _DeprecatedAPI_Description(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_Description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2793,8 +3556,8 @@ func (ec *executionContext) fieldContext_DeprecatedAPI_Description(ctx context.C return fc, nil } -func (ec *executionContext) _DeprecatedAPI_Kind(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_Kind(ctx, field) +func (ec *executionContext) _DeletedAPI_Version(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Version(ctx, field) if err != nil { return graphql.Null } @@ -2807,7 +3570,7 @@ func (ec *executionContext) _DeprecatedAPI_Kind(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Version, nil }) if err != nil { ec.Error(ctx, err) @@ -2821,9 +3584,9 @@ func (ec *executionContext) _DeprecatedAPI_Kind(ctx context.Context, field graph return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2834,8 +3597,8 @@ func (ec *executionContext) fieldContext_DeprecatedAPI_Kind(ctx context.Context, return fc, nil } -func (ec *executionContext) _DeprecatedAPI_Deprecated(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) +func (ec *executionContext) _DeletedAPI_Name(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Name(ctx, field) if err != nil { return graphql.Null } @@ -2848,7 +3611,7 @@ func (ec *executionContext) _DeprecatedAPI_Deprecated(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Deprecated, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -2857,26 +3620,26 @@ func (ec *executionContext) _DeprecatedAPI_Deprecated(ctx context.Context, field if resTmp == nil { return graphql.Null } - res := resTmp.(*bool) + res := resTmp.(*string) fc.Result = res - return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_Deprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _DeprecatedAPI_Scope(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_Scope(ctx, field) +func (ec *executionContext) _DeletedAPI_Deleted(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Deleted(ctx, field) if err != nil { return graphql.Null } @@ -2889,7 +3652,7 @@ func (ec *executionContext) _DeprecatedAPI_Scope(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Scope, nil + return obj.Deleted, nil }) if err != nil { ec.Error(ctx, err) @@ -2898,26 +3661,26 @@ func (ec *executionContext) _DeprecatedAPI_Scope(ctx context.Context, field grap if resTmp == nil { return graphql.Null } - res := resTmp.(*string) + res := resTmp.(*bool) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_Scope(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Deleted(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil } -func (ec *executionContext) _DeprecatedAPI_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) +func (ec *executionContext) _DeletedAPI_Scope(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_Scope(ctx, field) if err != nil { return graphql.Null } @@ -2930,7 +3693,7 @@ func (ec *executionContext) _DeprecatedAPI_EventTime(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.Scope, nil }) if err != nil { ec.Error(ctx, err) @@ -2944,9 +3707,9 @@ func (ec *executionContext) _DeprecatedAPI_EventTime(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_Scope(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2957,8 +3720,8 @@ func (ec *executionContext) fieldContext_DeprecatedAPI_EventTime(ctx context.Con return fc, nil } -func (ec *executionContext) _DeprecatedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) +func (ec *executionContext) _DeletedAPI_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -2971,7 +3734,7 @@ func (ec *executionContext) _DeprecatedAPI_ExpiryDate(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -2985,9 +3748,9 @@ func (ec *executionContext) _DeprecatedAPI_ExpiryDate(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_DeprecatedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "DeprecatedAPI", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -2998,8 +3761,8 @@ func (ec *executionContext) fieldContext_DeprecatedAPI_ExpiryDate(ctx context.Co return fc, nil } -func (ec *executionContext) _Event_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_ClusterName(ctx, field) +func (ec *executionContext) _DeletedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.DeletedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) if err != nil { return graphql.Null } @@ -3012,7 +3775,7 @@ func (ec *executionContext) _Event_ClusterName(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) @@ -3026,9 +3789,9 @@ func (ec *executionContext) _Event_ClusterName(ctx context.Context, field graphq return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeletedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeletedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3039,8 +3802,8 @@ func (ec *executionContext) fieldContext_Event_ClusterName(ctx context.Context, return fc, nil } -func (ec *executionContext) _Event_Id(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Id(ctx, field) +func (ec *executionContext) _DeprecatedAPI_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -3053,7 +3816,7 @@ func (ec *executionContext) _Event_Id(ctx context.Context, field graphql.Collect }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -3067,9 +3830,9 @@ func (ec *executionContext) _Event_Id(ctx context.Context, field graphql.Collect return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3080,8 +3843,8 @@ func (ec *executionContext) fieldContext_Event_Id(ctx context.Context, field gra return fc, nil } -func (ec *executionContext) _Event_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_EventTime(ctx, field) +func (ec *executionContext) _DeprecatedAPI_ObjectName(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) if err != nil { return graphql.Null } @@ -3094,7 +3857,7 @@ func (ec *executionContext) _Event_EventTime(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.ObjectName, nil }) if err != nil { ec.Error(ctx, err) @@ -3108,9 +3871,9 @@ func (ec *executionContext) _Event_EventTime(ctx context.Context, field graphql. return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_ObjectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3121,8 +3884,8 @@ func (ec *executionContext) fieldContext_Event_EventTime(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _Event_OpType(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_OpType(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Description(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Description(ctx, field) if err != nil { return graphql.Null } @@ -3135,7 +3898,7 @@ func (ec *executionContext) _Event_OpType(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.OpType, nil + return obj.Description, nil }) if err != nil { ec.Error(ctx, err) @@ -3149,9 +3912,9 @@ func (ec *executionContext) _Event_OpType(ctx context.Context, field graphql.Col return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_OpType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3162,8 +3925,8 @@ func (ec *executionContext) fieldContext_Event_OpType(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Event_Name(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Name(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Kind(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Kind(ctx, field) if err != nil { return graphql.Null } @@ -3176,7 +3939,7 @@ func (ec *executionContext) _Event_Name(ctx context.Context, field graphql.Colle }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -3190,9 +3953,9 @@ func (ec *executionContext) _Event_Name(ctx context.Context, field graphql.Colle return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3203,8 +3966,8 @@ func (ec *executionContext) fieldContext_Event_Name(ctx context.Context, field g return fc, nil } -func (ec *executionContext) _Event_Namespace(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Namespace(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Deprecated(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) if err != nil { return graphql.Null } @@ -3217,7 +3980,7 @@ func (ec *executionContext) _Event_Namespace(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.Deprecated, nil }) if err != nil { ec.Error(ctx, err) @@ -3226,26 +3989,26 @@ func (ec *executionContext) _Event_Namespace(ctx context.Context, field graphql. if resTmp == nil { return graphql.Null } - res := resTmp.(*string) + res := resTmp.(*bool) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Deprecated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Event_Kind(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Kind(ctx, field) +func (ec *executionContext) _DeprecatedAPI_Scope(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_Scope(ctx, field) if err != nil { return graphql.Null } @@ -3258,7 +4021,7 @@ func (ec *executionContext) _Event_Kind(ctx context.Context, field graphql.Colle }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Scope, nil }) if err != nil { ec.Error(ctx, err) @@ -3272,9 +4035,9 @@ func (ec *executionContext) _Event_Kind(ctx context.Context, field graphql.Colle return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_Scope(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3285,8 +4048,8 @@ func (ec *executionContext) fieldContext_Event_Kind(ctx context.Context, field g return fc, nil } -func (ec *executionContext) _Event_Message(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Message(ctx, field) +func (ec *executionContext) _DeprecatedAPI_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -3299,7 +4062,7 @@ func (ec *executionContext) _Event_Message(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Message, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -3313,9 +4076,9 @@ func (ec *executionContext) _Event_Message(ctx context.Context, field graphql.Co return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3326,9 +4089,9 @@ func (ec *executionContext) fieldContext_Event_Message(ctx context.Context, fiel return fc, nil } -func (ec *executionContext) _Event_Reason(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Reason(ctx, field) - if err != nil { +func (ec *executionContext) _DeprecatedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.DeprecatedAPI) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) + if err != nil { return graphql.Null } ctx = graphql.WithFieldContext(ctx, fc) @@ -3340,7 +4103,7 @@ func (ec *executionContext) _Event_Reason(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Reason, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) @@ -3354,9 +4117,9 @@ func (ec *executionContext) _Event_Reason(ctx context.Context, field graphql.Col return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Reason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_DeprecatedAPI_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Event", + Object: "DeprecatedAPI", Field: field, IsMethod: false, IsResolver: false, @@ -3367,8 +4130,8 @@ func (ec *executionContext) fieldContext_Event_Reason(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Event_Host(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Host(ctx, field) +func (ec *executionContext) _Event_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -3381,7 +4144,7 @@ func (ec *executionContext) _Event_Host(ctx context.Context, field graphql.Colle }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Host, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -3395,7 +4158,7 @@ func (ec *executionContext) _Event_Host(ctx context.Context, field graphql.Colle return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Host(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Event", Field: field, @@ -3408,8 +4171,8 @@ func (ec *executionContext) fieldContext_Event_Host(ctx context.Context, field g return fc, nil } -func (ec *executionContext) _Event_Event(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_Event(ctx, field) +func (ec *executionContext) _Event_Id(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Id(ctx, field) if err != nil { return graphql.Null } @@ -3422,7 +4185,7 @@ func (ec *executionContext) _Event_Event(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Event, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -3436,7 +4199,7 @@ func (ec *executionContext) _Event_Event(ctx context.Context, field graphql.Coll return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_Event(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Event", Field: field, @@ -3449,8 +4212,8 @@ func (ec *executionContext) fieldContext_Event_Event(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Event_ImageName(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_ImageName(ctx, field) +func (ec *executionContext) _Event_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -3463,7 +4226,7 @@ func (ec *executionContext) _Event_ImageName(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ImageName, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -3477,7 +4240,7 @@ func (ec *executionContext) _Event_ImageName(ctx context.Context, field graphql. return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_ImageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Event", Field: field, @@ -3490,8 +4253,8 @@ func (ec *executionContext) fieldContext_Event_ImageName(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _Event_FirstTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_FirstTime(ctx, field) +func (ec *executionContext) _Event_OpType(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_OpType(ctx, field) if err != nil { return graphql.Null } @@ -3504,7 +4267,7 @@ func (ec *executionContext) _Event_FirstTime(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.FirstTime, nil + return obj.OpType, nil }) if err != nil { ec.Error(ctx, err) @@ -3518,7 +4281,7 @@ func (ec *executionContext) _Event_FirstTime(ctx context.Context, field graphql. return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_FirstTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_OpType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Event", Field: field, @@ -3531,8 +4294,8 @@ func (ec *executionContext) fieldContext_Event_FirstTime(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _Event_LastTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_LastTime(ctx, field) +func (ec *executionContext) _Event_Name(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Name(ctx, field) if err != nil { return graphql.Null } @@ -3545,7 +4308,7 @@ func (ec *executionContext) _Event_LastTime(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.LastTime, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -3559,7 +4322,7 @@ func (ec *executionContext) _Event_LastTime(ctx context.Context, field graphql.C return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_LastTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Event", Field: field, @@ -3572,8 +4335,8 @@ func (ec *executionContext) fieldContext_Event_LastTime(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Event_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Event_ExpiryDate(ctx, field) +func (ec *executionContext) _Event_Namespace(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Namespace(ctx, field) if err != nil { return graphql.Null } @@ -3586,7 +4349,7 @@ func (ec *executionContext) _Event_ExpiryDate(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) @@ -3600,7 +4363,7 @@ func (ec *executionContext) _Event_ExpiryDate(ctx context.Context, field graphql return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Event_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Event", Field: field, @@ -3613,8 +4376,8 @@ func (ec *executionContext) fieldContext_Event_ExpiryDate(ctx context.Context, f return fc, nil } -func (ec *executionContext) _GetAllResource_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_ClusterName(ctx, field) +func (ec *executionContext) _Event_Kind(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Kind(ctx, field) if err != nil { return graphql.Null } @@ -3627,7 +4390,7 @@ func (ec *executionContext) _GetAllResource_ClusterName(ctx context.Context, fie }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -3641,9 +4404,9 @@ func (ec *executionContext) _GetAllResource_ClusterName(ctx context.Context, fie return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3654,8 +4417,8 @@ func (ec *executionContext) fieldContext_GetAllResource_ClusterName(ctx context. return fc, nil } -func (ec *executionContext) _GetAllResource_Namespace(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_Namespace(ctx, field) +func (ec *executionContext) _Event_Message(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Message(ctx, field) if err != nil { return graphql.Null } @@ -3668,7 +4431,7 @@ func (ec *executionContext) _GetAllResource_Namespace(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.Message, nil }) if err != nil { ec.Error(ctx, err) @@ -3682,9 +4445,9 @@ func (ec *executionContext) _GetAllResource_Namespace(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_Namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3695,8 +4458,8 @@ func (ec *executionContext) fieldContext_GetAllResource_Namespace(ctx context.Co return fc, nil } -func (ec *executionContext) _GetAllResource_Kind(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_Kind(ctx, field) +func (ec *executionContext) _Event_Reason(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Reason(ctx, field) if err != nil { return graphql.Null } @@ -3709,7 +4472,7 @@ func (ec *executionContext) _GetAllResource_Kind(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Reason, nil }) if err != nil { ec.Error(ctx, err) @@ -3723,9 +4486,9 @@ func (ec *executionContext) _GetAllResource_Kind(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Reason(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3736,8 +4499,8 @@ func (ec *executionContext) fieldContext_GetAllResource_Kind(ctx context.Context return fc, nil } -func (ec *executionContext) _GetAllResource_Resource(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_Resource(ctx, field) +func (ec *executionContext) _Event_Host(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Host(ctx, field) if err != nil { return graphql.Null } @@ -3750,7 +4513,7 @@ func (ec *executionContext) _GetAllResource_Resource(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Resource, nil + return obj.Host, nil }) if err != nil { ec.Error(ctx, err) @@ -3764,9 +4527,9 @@ func (ec *executionContext) _GetAllResource_Resource(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_Resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Host(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3777,8 +4540,8 @@ func (ec *executionContext) fieldContext_GetAllResource_Resource(ctx context.Con return fc, nil } -func (ec *executionContext) _GetAllResource_Age(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_Age(ctx, field) +func (ec *executionContext) _Event_Event(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_Event(ctx, field) if err != nil { return graphql.Null } @@ -3791,7 +4554,7 @@ func (ec *executionContext) _GetAllResource_Age(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Age, nil + return obj.Event, nil }) if err != nil { ec.Error(ctx, err) @@ -3805,9 +4568,9 @@ func (ec *executionContext) _GetAllResource_Age(ctx context.Context, field graph return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_Age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_Event(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3818,8 +4581,8 @@ func (ec *executionContext) fieldContext_GetAllResource_Age(ctx context.Context, return fc, nil } -func (ec *executionContext) _GetAllResource_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_EventTime(ctx, field) +func (ec *executionContext) _Event_ImageName(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_ImageName(ctx, field) if err != nil { return graphql.Null } @@ -3832,7 +4595,7 @@ func (ec *executionContext) _GetAllResource_EventTime(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.ImageName, nil }) if err != nil { ec.Error(ctx, err) @@ -3846,9 +4609,9 @@ func (ec *executionContext) _GetAllResource_EventTime(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_ImageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3859,8 +4622,8 @@ func (ec *executionContext) fieldContext_GetAllResource_EventTime(ctx context.Co return fc, nil } -func (ec *executionContext) _GetAllResource_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) +func (ec *executionContext) _Event_FirstTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_FirstTime(ctx, field) if err != nil { return graphql.Null } @@ -3873,7 +4636,7 @@ func (ec *executionContext) _GetAllResource_ExpiryDate(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.FirstTime, nil }) if err != nil { ec.Error(ctx, err) @@ -3887,9 +4650,9 @@ func (ec *executionContext) _GetAllResource_ExpiryDate(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GetAllResource_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_FirstTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GetAllResource", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3900,8 +4663,8 @@ func (ec *executionContext) fieldContext_GetAllResource_ExpiryDate(ctx context.C return fc, nil } -func (ec *executionContext) _KubeScore_id(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_id(ctx, field) +func (ec *executionContext) _Event_LastTime(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_LastTime(ctx, field) if err != nil { return graphql.Null } @@ -3914,38 +4677,35 @@ func (ec *executionContext) _KubeScore_id(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return obj.LastTime, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_LastTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _KubeScore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_clusterName(ctx, field) +func (ec *executionContext) _Event_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Event_ExpiryDate(ctx, field) if err != nil { return graphql.Null } @@ -3958,26 +4718,23 @@ func (ec *executionContext) _KubeScore_clusterName(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Event_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "Event", Field: field, IsMethod: false, IsResolver: false, @@ -3988,8 +4745,8 @@ func (ec *executionContext) fieldContext_KubeScore_clusterName(ctx context.Conte return fc, nil } -func (ec *executionContext) _KubeScore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_objectName(ctx, field) +func (ec *executionContext) _GetAllResource_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_ClusterName(ctx, field) if err != nil { return graphql.Null } @@ -4002,26 +4759,23 @@ func (ec *executionContext) _KubeScore_objectName(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ObjectName, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4032,8 +4786,8 @@ func (ec *executionContext) fieldContext_KubeScore_objectName(ctx context.Contex return fc, nil } -func (ec *executionContext) _KubeScore_kind(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_kind(ctx, field) +func (ec *executionContext) _GetAllResource_Namespace(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Namespace(ctx, field) if err != nil { return graphql.Null } @@ -4046,26 +4800,23 @@ func (ec *executionContext) _KubeScore_kind(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_Namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4076,8 +4827,8 @@ func (ec *executionContext) fieldContext_KubeScore_kind(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_apiVersion(ctx, field) +func (ec *executionContext) _GetAllResource_Kind(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Kind(ctx, field) if err != nil { return graphql.Null } @@ -4090,26 +4841,23 @@ func (ec *executionContext) _KubeScore_apiVersion(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.APIVersion, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_Kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4120,8 +4868,8 @@ func (ec *executionContext) fieldContext_KubeScore_apiVersion(ctx context.Contex return fc, nil } -func (ec *executionContext) _KubeScore_name(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_name(ctx, field) +func (ec *executionContext) _GetAllResource_Resource(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Resource(ctx, field) if err != nil { return graphql.Null } @@ -4134,26 +4882,23 @@ func (ec *executionContext) _KubeScore_name(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Resource, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_Resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4164,8 +4909,8 @@ func (ec *executionContext) fieldContext_KubeScore_name(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _KubeScore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_namespace(ctx, field) +func (ec *executionContext) _GetAllResource_Age(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_Age(ctx, field) if err != nil { return graphql.Null } @@ -4178,26 +4923,23 @@ func (ec *executionContext) _KubeScore_namespace(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.Age, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_Age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4208,8 +4950,8 @@ func (ec *executionContext) fieldContext_KubeScore_namespace(ctx context.Context return fc, nil } -func (ec *executionContext) _KubeScore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_targetType(ctx, field) +func (ec *executionContext) _GetAllResource_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_EventTime(ctx, field) if err != nil { return graphql.Null } @@ -4222,26 +4964,23 @@ func (ec *executionContext) _KubeScore_targetType(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.TargetType, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4252,8 +4991,8 @@ func (ec *executionContext) fieldContext_KubeScore_targetType(ctx context.Contex return fc, nil } -func (ec *executionContext) _KubeScore_description(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_description(ctx, field) +func (ec *executionContext) _GetAllResource_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.GetAllResource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) if err != nil { return graphql.Null } @@ -4266,26 +5005,23 @@ func (ec *executionContext) _KubeScore_description(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GetAllResource_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "KubeScore", + Object: "GetAllResource", Field: field, IsMethod: false, IsResolver: false, @@ -4296,8 +5032,8 @@ func (ec *executionContext) fieldContext_KubeScore_description(ctx context.Conte return fc, nil } -func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_path(ctx, field) +func (ec *executionContext) _KubeScore_id(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_id(ctx, field) if err != nil { return graphql.Null } @@ -4310,7 +5046,7 @@ func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Path, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -4324,24 +5060,24 @@ func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.C } res := resTmp.(string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_summary(ctx, field) +func (ec *executionContext) _KubeScore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -4354,7 +5090,7 @@ func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Summary, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -4371,7 +5107,7 @@ func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphq return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "KubeScore", Field: field, @@ -4384,8 +5120,8 @@ func (ec *executionContext) fieldContext_KubeScore_summary(ctx context.Context, return fc, nil } -func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_fileName(ctx, field) +func (ec *executionContext) _KubeScore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_objectName(ctx, field) if err != nil { return graphql.Null } @@ -4398,7 +5134,7 @@ func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.FileName, nil + return obj.ObjectName, nil }) if err != nil { ec.Error(ctx, err) @@ -4415,7 +5151,7 @@ func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graph return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "KubeScore", Field: field, @@ -4428,8 +5164,8 @@ func (ec *executionContext) fieldContext_KubeScore_fileName(ctx context.Context, return fc, nil } -func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_fileRow(ctx, field) +func (ec *executionContext) _KubeScore_kind(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_kind(ctx, field) if err != nil { return graphql.Null } @@ -4442,7 +5178,7 @@ func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.FileRow, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -4454,26 +5190,26 @@ func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphq } return graphql.Null } - res := resTmp.(int) + res := resTmp.(string) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_KubeScore_eventTime(ctx, field) +func (ec *executionContext) _KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_apiVersion(ctx, field) if err != nil { return graphql.Null } @@ -4486,7 +5222,7 @@ func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.APIVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -4503,7 +5239,7 @@ func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field grap return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_KubeScore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "KubeScore", Field: field, @@ -4516,8 +5252,8 @@ func (ec *executionContext) fieldContext_KubeScore_eventTime(ctx context.Context return fc, nil } -func (ec *executionContext) _Kubescore_id(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_id(ctx, field) +func (ec *executionContext) _KubeScore_name(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_name(ctx, field) if err != nil { return graphql.Null } @@ -4530,7 +5266,7 @@ func (ec *executionContext) _Kubescore_id(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -4544,24 +5280,24 @@ func (ec *executionContext) _Kubescore_id(ctx context.Context, field graphql.Col } res := resTmp.(string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Kubescore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_clusterName(ctx, field) +func (ec *executionContext) _KubeScore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_namespace(ctx, field) if err != nil { return graphql.Null } @@ -4574,23 +5310,26 @@ func (ec *executionContext) _Kubescore_clusterName(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4601,8 +5340,8 @@ func (ec *executionContext) fieldContext_Kubescore_clusterName(ctx context.Conte return fc, nil } -func (ec *executionContext) _Kubescore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_objectName(ctx, field) +func (ec *executionContext) _KubeScore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_targetType(ctx, field) if err != nil { return graphql.Null } @@ -4615,23 +5354,26 @@ func (ec *executionContext) _Kubescore_objectName(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ObjectName, nil + return obj.TargetType, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4642,8 +5384,8 @@ func (ec *executionContext) fieldContext_Kubescore_objectName(ctx context.Contex return fc, nil } -func (ec *executionContext) _Kubescore_kind(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_kind(ctx, field) +func (ec *executionContext) _KubeScore_description(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_description(ctx, field) if err != nil { return graphql.Null } @@ -4656,23 +5398,26 @@ func (ec *executionContext) _Kubescore_kind(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Description, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4683,8 +5428,8 @@ func (ec *executionContext) fieldContext_Kubescore_kind(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Kubescore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_apiVersion(ctx, field) +func (ec *executionContext) _KubeScore_path(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_path(ctx, field) if err != nil { return graphql.Null } @@ -4697,23 +5442,26 @@ func (ec *executionContext) _Kubescore_apiVersion(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.APIVersion, nil + return obj.Path, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4724,8 +5472,8 @@ func (ec *executionContext) fieldContext_Kubescore_apiVersion(ctx context.Contex return fc, nil } -func (ec *executionContext) _Kubescore_name(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_name(ctx, field) +func (ec *executionContext) _KubeScore_summary(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_summary(ctx, field) if err != nil { return graphql.Null } @@ -4738,23 +5486,26 @@ func (ec *executionContext) _Kubescore_name(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Summary, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4765,8 +5516,8 @@ func (ec *executionContext) fieldContext_Kubescore_name(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Kubescore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_namespace(ctx, field) +func (ec *executionContext) _KubeScore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_fileName(ctx, field) if err != nil { return graphql.Null } @@ -4779,23 +5530,26 @@ func (ec *executionContext) _Kubescore_namespace(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.FileName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4806,8 +5560,8 @@ func (ec *executionContext) fieldContext_Kubescore_namespace(ctx context.Context return fc, nil } -func (ec *executionContext) _Kubescore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_targetType(ctx, field) +func (ec *executionContext) _KubeScore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_fileRow(ctx, field) if err != nil { return graphql.Null } @@ -4820,35 +5574,38 @@ func (ec *executionContext) _Kubescore_targetType(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.TargetType, nil + return obj.FileRow, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(int) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Kubescore_description(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_description(ctx, field) +func (ec *executionContext) _KubeScore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.KubeScore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_KubeScore_eventTime(ctx, field) if err != nil { return graphql.Null } @@ -4861,23 +5618,26 @@ func (ec *executionContext) _Kubescore_description(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_KubeScore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Kubescore", + Object: "KubeScore", Field: field, IsMethod: false, IsResolver: false, @@ -4888,8 +5648,8 @@ func (ec *executionContext) fieldContext_Kubescore_description(ctx context.Conte return fc, nil } -func (ec *executionContext) _Kubescore_path(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_path(ctx, field) +func (ec *executionContext) _Kubescore_id(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_id(ctx, field) if err != nil { return graphql.Null } @@ -4902,35 +5662,38 @@ func (ec *executionContext) _Kubescore_path(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Path, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Kubescore_summary(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_summary(ctx, field) +func (ec *executionContext) _Kubescore_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -4943,7 +5706,7 @@ func (ec *executionContext) _Kubescore_summary(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Summary, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -4957,7 +5720,7 @@ func (ec *executionContext) _Kubescore_summary(ctx context.Context, field graphq return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Kubescore", Field: field, @@ -4970,9 +5733,9 @@ func (ec *executionContext) fieldContext_Kubescore_summary(ctx context.Context, return fc, nil } -func (ec *executionContext) _Kubescore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_fileName(ctx, field) - if err != nil { +func (ec *executionContext) _Kubescore_objectName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_objectName(ctx, field) + if err != nil { return graphql.Null } ctx = graphql.WithFieldContext(ctx, fc) @@ -4984,7 +5747,7 @@ func (ec *executionContext) _Kubescore_fileName(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.FileName, nil + return obj.ObjectName, nil }) if err != nil { ec.Error(ctx, err) @@ -4998,7 +5761,7 @@ func (ec *executionContext) _Kubescore_fileName(ctx context.Context, field graph return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_objectName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Kubescore", Field: field, @@ -5011,8 +5774,8 @@ func (ec *executionContext) fieldContext_Kubescore_fileName(ctx context.Context, return fc, nil } -func (ec *executionContext) _Kubescore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_fileRow(ctx, field) +func (ec *executionContext) _Kubescore_kind(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_kind(ctx, field) if err != nil { return graphql.Null } @@ -5025,7 +5788,7 @@ func (ec *executionContext) _Kubescore_fileRow(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.FileRow, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -5034,26 +5797,26 @@ func (ec *executionContext) _Kubescore_fileRow(ctx context.Context, field graphq if resTmp == nil { return graphql.Null } - res := resTmp.(*int) + res := resTmp.(*string) fc.Result = res - return ec.marshalOInt2ᚖint(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Kubescore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_eventTime(ctx, field) +func (ec *executionContext) _Kubescore_apiVersion(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_apiVersion(ctx, field) if err != nil { return graphql.Null } @@ -5066,7 +5829,7 @@ func (ec *executionContext) _Kubescore_eventTime(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.APIVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -5080,7 +5843,7 @@ func (ec *executionContext) _Kubescore_eventTime(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_apiVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Kubescore", Field: field, @@ -5093,8 +5856,8 @@ func (ec *executionContext) fieldContext_Kubescore_eventTime(ctx context.Context return fc, nil } -func (ec *executionContext) _Kubescore_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Kubescore_expiryDate(ctx, field) +func (ec *executionContext) _Kubescore_name(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_name(ctx, field) if err != nil { return graphql.Null } @@ -5107,7 +5870,7 @@ func (ec *executionContext) _Kubescore_expiryDate(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -5121,7 +5884,7 @@ func (ec *executionContext) _Kubescore_expiryDate(ctx context.Context, field gra return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Kubescore_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Kubescore", Field: field, @@ -5134,8 +5897,8 @@ func (ec *executionContext) fieldContext_Kubescore_expiryDate(ctx context.Contex return fc, nil } -func (ec *executionContext) _Namespace_name(ctx context.Context, field graphql.CollectedField, obj *model.Namespace) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Namespace_name(ctx, field) +func (ec *executionContext) _Kubescore_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_namespace(ctx, field) if err != nil { return graphql.Null } @@ -5148,26 +5911,23 @@ func (ec *executionContext) _Namespace_name(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Namespace_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Namespace", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, @@ -5178,8 +5938,8 @@ func (ec *executionContext) fieldContext_Namespace_name(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_namespace(ctx, field) +func (ec *executionContext) _Kubescore_targetType(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_targetType(ctx, field) if err != nil { return graphql.Null } @@ -5192,26 +5952,23 @@ func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.TargetType, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_NamespaceData_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_targetType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "NamespaceData", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, @@ -5222,8 +5979,8 @@ func (ec *executionContext) fieldContext_NamespaceData_namespace(ctx context.Con return fc, nil } -func (ec *executionContext) _NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_outdatedImages(ctx, field) +func (ec *executionContext) _Kubescore_description(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_description(ctx, field) if err != nil { return graphql.Null } @@ -5236,56 +5993,35 @@ func (ec *executionContext) _NamespaceData_outdatedImages(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.OutdatedImages, nil + return obj.Description, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.OutdatedImage) + res := resTmp.(*string) fc.Result = res - return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "NamespaceData", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_OutdatedImage_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_OutdatedImage_namespace(ctx, field) - case "pod": - return ec.fieldContext_OutdatedImage_pod(ctx, field) - case "currentImage": - return ec.fieldContext_OutdatedImage_currentImage(ctx, field) - case "currentTag": - return ec.fieldContext_OutdatedImage_currentTag(ctx, field) - case "latestVersion": - return ec.fieldContext_OutdatedImage_latestVersion(ctx, field) - case "versionsBehind": - return ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) - case "eventTime": - return ec.fieldContext_OutdatedImage_eventTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_kubeScores(ctx, field) +func (ec *executionContext) _Kubescore_path(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_path(ctx, field) if err != nil { return graphql.Null } @@ -5298,68 +6034,35 @@ func (ec *executionContext) _NamespaceData_kubeScores(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.KubeScores, nil + return obj.Path, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.KubeScore) + res := resTmp.(*string) fc.Result = res - return ec.marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_path(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "NamespaceData", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_KubeScore_id(ctx, field) - case "clusterName": - return ec.fieldContext_KubeScore_clusterName(ctx, field) - case "objectName": - return ec.fieldContext_KubeScore_objectName(ctx, field) - case "kind": - return ec.fieldContext_KubeScore_kind(ctx, field) - case "apiVersion": - return ec.fieldContext_KubeScore_apiVersion(ctx, field) - case "name": - return ec.fieldContext_KubeScore_name(ctx, field) - case "namespace": - return ec.fieldContext_KubeScore_namespace(ctx, field) - case "targetType": - return ec.fieldContext_KubeScore_targetType(ctx, field) - case "description": - return ec.fieldContext_KubeScore_description(ctx, field) - case "path": - return ec.fieldContext_KubeScore_path(ctx, field) - case "summary": - return ec.fieldContext_KubeScore_summary(ctx, field) - case "fileName": - return ec.fieldContext_KubeScore_fileName(ctx, field) - case "fileRow": - return ec.fieldContext_KubeScore_fileRow(ctx, field) - case "eventTime": - return ec.fieldContext_KubeScore_eventTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type KubeScore", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _NamespaceData_resources(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NamespaceData_resources(ctx, field) +func (ec *executionContext) _Kubescore_summary(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_summary(ctx, field) if err != nil { return graphql.Null } @@ -5372,52 +6075,35 @@ func (ec *executionContext) _NamespaceData_resources(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Resources, nil + return obj.Summary, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.Resource) + res := resTmp.(*string) fc.Result = res - return ec.marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_NamespaceData_resources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_summary(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "NamespaceData", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_Resource_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_Resource_namespace(ctx, field) - case "kind": - return ec.fieldContext_Resource_kind(ctx, field) - case "resource": - return ec.fieldContext_Resource_resource(ctx, field) - case "age": - return ec.fieldContext_Resource_age(ctx, field) - case "eventTime": - return ec.fieldContext_Resource_eventTime(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Resource", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_clusterName(ctx, field) +func (ec *executionContext) _Kubescore_fileName(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_fileName(ctx, field) if err != nil { return graphql.Null } @@ -5430,26 +6116,23 @@ func (ec *executionContext) _OutdatedImage_clusterName(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.FileName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_fileName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, @@ -5460,8 +6143,8 @@ func (ec *executionContext) fieldContext_OutdatedImage_clusterName(ctx context.C return fc, nil } -func (ec *executionContext) _OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_namespace(ctx, field) +func (ec *executionContext) _Kubescore_fileRow(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_fileRow(ctx, field) if err != nil { return graphql.Null } @@ -5474,38 +6157,35 @@ func (ec *executionContext) _OutdatedImage_namespace(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.FileRow, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*int) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_fileRow(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _OutdatedImage_pod(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_pod(ctx, field) +func (ec *executionContext) _Kubescore_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Kubescore) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Kubescore_eventTime(ctx, field) if err != nil { return graphql.Null } @@ -5518,26 +6198,23 @@ func (ec *executionContext) _OutdatedImage_pod(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Pod, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_pod(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Kubescore_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Kubescore", Field: field, IsMethod: false, IsResolver: false, @@ -5548,8 +6225,8 @@ func (ec *executionContext) fieldContext_OutdatedImage_pod(ctx context.Context, return fc, nil } -func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_currentImage(ctx, field) +func (ec *executionContext) _Misconfiguration_id(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_id(ctx, field) if err != nil { return graphql.Null } @@ -5562,7 +6239,7 @@ func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, fie }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.CurrentImage, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -5576,24 +6253,24 @@ func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, fie } res := resTmp.(string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Misconfiguration", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_currentTag(ctx, field) +func (ec *executionContext) _Misconfiguration_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -5606,7 +6283,7 @@ func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.CurrentTag, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -5623,9 +6300,9 @@ func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Misconfiguration", Field: field, IsMethod: false, IsResolver: false, @@ -5636,8 +6313,8 @@ func (ec *executionContext) fieldContext_OutdatedImage_currentTag(ctx context.Co return fc, nil } -func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_latestVersion(ctx, field) +func (ec *executionContext) _Misconfiguration_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_namespace(ctx, field) if err != nil { return graphql.Null } @@ -5650,7 +6327,7 @@ func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.LatestVersion, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) @@ -5667,9 +6344,9 @@ func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, fi return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Misconfiguration", Field: field, IsMethod: false, IsResolver: false, @@ -5680,8 +6357,8 @@ func (ec *executionContext) fieldContext_OutdatedImage_latestVersion(ctx context return fc, nil } -func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) +func (ec *executionContext) _Misconfiguration_kind(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_kind(ctx, field) if err != nil { return graphql.Null } @@ -5694,7 +6371,7 @@ func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VersionsBehind, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -5706,26 +6383,26 @@ func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, f } return graphql.Null } - res := resTmp.(int) + res := resTmp.(string) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Misconfiguration", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_OutdatedImage_eventTime(ctx, field) +func (ec *executionContext) _Misconfiguration_name(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_name(ctx, field) if err != nil { return graphql.Null } @@ -5738,7 +6415,7 @@ func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -5755,9 +6432,9 @@ func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "OutdatedImage", + Object: "Misconfiguration", Field: field, IsMethod: false, IsResolver: false, @@ -5768,8 +6445,8 @@ func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Con return fc, nil } -func (ec *executionContext) _Query_allEvents(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allEvents(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigId(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigId(ctx, field) if err != nil { return graphql.Null } @@ -5782,7 +6459,7 @@ func (ec *executionContext) _Query_allEvents(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllEvents(rctx) + return obj.MisconfigID, nil }) if err != nil { ec.Error(ctx, err) @@ -5794,58 +6471,26 @@ func (ec *executionContext) _Query_allEvents(ctx context.Context, field graphql. } return graphql.Null } - res := resTmp.([]*model.Event) + res := resTmp.(string) fc.Result = res - return ec.marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allEvents(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "ClusterName": - return ec.fieldContext_Event_ClusterName(ctx, field) - case "Id": - return ec.fieldContext_Event_Id(ctx, field) - case "EventTime": - return ec.fieldContext_Event_EventTime(ctx, field) - case "OpType": - return ec.fieldContext_Event_OpType(ctx, field) - case "Name": - return ec.fieldContext_Event_Name(ctx, field) - case "Namespace": - return ec.fieldContext_Event_Namespace(ctx, field) - case "Kind": - return ec.fieldContext_Event_Kind(ctx, field) - case "Message": - return ec.fieldContext_Event_Message(ctx, field) - case "Reason": - return ec.fieldContext_Event_Reason(ctx, field) - case "Host": - return ec.fieldContext_Event_Host(ctx, field) - case "Event": - return ec.fieldContext_Event_Event(ctx, field) - case "ImageName": - return ec.fieldContext_Event_ImageName(ctx, field) - case "FirstTime": - return ec.fieldContext_Event_FirstTime(ctx, field) - case "LastTime": - return ec.fieldContext_Event_LastTime(ctx, field) - case "ExpiryDate": - return ec.fieldContext_Event_ExpiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Event", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allRakkess(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allRakkess(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigAvdid(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigAvdid(ctx, field) if err != nil { return graphql.Null } @@ -5858,56 +6503,35 @@ func (ec *executionContext) _Query_allRakkess(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllRakkess(rctx) + return obj.MisconfigAvdid, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.Rakkess) + res := resTmp.(*string) fc.Result = res - return ec.marshalNRakkess2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐRakkessᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allRakkess(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigAvdid(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "ClusterName": - return ec.fieldContext_Rakkess_ClusterName(ctx, field) - case "Name": - return ec.fieldContext_Rakkess_Name(ctx, field) - case "Create": - return ec.fieldContext_Rakkess_Create(ctx, field) - case "Delete": - return ec.fieldContext_Rakkess_Delete(ctx, field) - case "List": - return ec.fieldContext_Rakkess_List(ctx, field) - case "Update": - return ec.fieldContext_Rakkess_Update(ctx, field) - case "EventTime": - return ec.fieldContext_Rakkess_EventTime(ctx, field) - case "ExpiryDate": - return ec.fieldContext_Rakkess_ExpiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Rakkess", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allDeprecatedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allDeprecatedAPIs(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigType(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigType(ctx, field) if err != nil { return graphql.Null } @@ -5920,56 +6544,35 @@ func (ec *executionContext) _Query_allDeprecatedAPIs(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllDeprecatedAPIs(rctx) + return obj.MisconfigType, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.DeprecatedAPI) + res := resTmp.(*string) fc.Result = res - return ec.marshalNDeprecatedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPIᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allDeprecatedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "ClusterName": - return ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) - case "ObjectName": - return ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) - case "Description": - return ec.fieldContext_DeprecatedAPI_Description(ctx, field) - case "Kind": - return ec.fieldContext_DeprecatedAPI_Kind(ctx, field) - case "Deprecated": - return ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) - case "Scope": - return ec.fieldContext_DeprecatedAPI_Scope(ctx, field) - case "EventTime": - return ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) - case "ExpiryDate": - return ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type DeprecatedAPI", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allDeletedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allDeletedAPIs(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigTitle(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigTitle(ctx, field) if err != nil { return graphql.Null } @@ -5982,60 +6585,35 @@ func (ec *executionContext) _Query_allDeletedAPIs(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllDeletedAPIs(rctx) + return obj.MisconfigTitle, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.DeletedAPI) + res := resTmp.(*string) fc.Result = res - return ec.marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allDeletedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "ClusterName": - return ec.fieldContext_DeletedAPI_ClusterName(ctx, field) - case "ObjectName": - return ec.fieldContext_DeletedAPI_ObjectName(ctx, field) - case "Group": - return ec.fieldContext_DeletedAPI_Group(ctx, field) - case "Kind": - return ec.fieldContext_DeletedAPI_Kind(ctx, field) - case "Version": - return ec.fieldContext_DeletedAPI_Version(ctx, field) - case "Name": - return ec.fieldContext_DeletedAPI_Name(ctx, field) - case "Deleted": - return ec.fieldContext_DeletedAPI_Deleted(ctx, field) - case "Scope": - return ec.fieldContext_DeletedAPI_Scope(ctx, field) - case "EventTime": - return ec.fieldContext_DeletedAPI_EventTime(ctx, field) - case "ExpiryDate": - return ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type DeletedAPI", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allGetAllResources(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allGetAllResources(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigDesc(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigDesc(ctx, field) if err != nil { return graphql.Null } @@ -6048,54 +6626,35 @@ func (ec *executionContext) _Query_allGetAllResources(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllGetAllResources(rctx) + return obj.MisconfigDesc, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.GetAllResource) + res := resTmp.(*string) fc.Result = res - return ec.marshalNGetAllResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResourceᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allGetAllResources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigDesc(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "ClusterName": - return ec.fieldContext_GetAllResource_ClusterName(ctx, field) - case "Namespace": - return ec.fieldContext_GetAllResource_Namespace(ctx, field) - case "Kind": - return ec.fieldContext_GetAllResource_Kind(ctx, field) - case "Resource": - return ec.fieldContext_GetAllResource_Resource(ctx, field) - case "Age": - return ec.fieldContext_GetAllResource_Age(ctx, field) - case "EventTime": - return ec.fieldContext_GetAllResource_EventTime(ctx, field) - case "ExpiryDate": - return ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type GetAllResource", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allTrivySBOMs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allTrivySBOMs(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigMsg(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigMsg(ctx, field) if err != nil { return graphql.Null } @@ -6108,60 +6667,35 @@ func (ec *executionContext) _Query_allTrivySBOMs(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllTrivySBOMs(rctx) + return obj.MisconfigMsg, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.TrivySbom) + res := resTmp.(*string) fc.Result = res - return ec.marshalNTrivySBOM2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbomᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allTrivySBOMs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigMsg(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_TrivySBOM_id(ctx, field) - case "clusterName": - return ec.fieldContext_TrivySBOM_clusterName(ctx, field) - case "imageName": - return ec.fieldContext_TrivySBOM_imageName(ctx, field) - case "packageName": - return ec.fieldContext_TrivySBOM_packageName(ctx, field) - case "packageUrl": - return ec.fieldContext_TrivySBOM_packageUrl(ctx, field) - case "bomRef": - return ec.fieldContext_TrivySBOM_bomRef(ctx, field) - case "serialNumber": - return ec.fieldContext_TrivySBOM_serialNumber(ctx, field) - case "version": - return ec.fieldContext_TrivySBOM_version(ctx, field) - case "bomFormat": - return ec.fieldContext_TrivySBOM_bomFormat(ctx, field) - case "expiryDate": - return ec.fieldContext_TrivySBOM_expiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type TrivySBOM", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allTrivyImages(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allTrivyImages(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigQuery(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigQuery(ctx, field) if err != nil { return graphql.Null } @@ -6174,66 +6708,35 @@ func (ec *executionContext) _Query_allTrivyImages(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllTrivyImages(rctx) + return obj.MisconfigQuery, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.TrivyImage) + res := resTmp.(*string) fc.Result = res - return ec.marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allTrivyImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigQuery(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_TrivyImage_id(ctx, field) - case "clusterName": - return ec.fieldContext_TrivyImage_clusterName(ctx, field) - case "artifactName": - return ec.fieldContext_TrivyImage_artifactName(ctx, field) - case "vulId": - return ec.fieldContext_TrivyImage_vulId(ctx, field) - case "vulPkgId": - return ec.fieldContext_TrivyImage_vulPkgId(ctx, field) - case "vulPkgName": - return ec.fieldContext_TrivyImage_vulPkgName(ctx, field) - case "vulInstalledVersion": - return ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) - case "vulFixedVersion": - return ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) - case "vulTitle": - return ec.fieldContext_TrivyImage_vulTitle(ctx, field) - case "vulSeverity": - return ec.fieldContext_TrivyImage_vulSeverity(ctx, field) - case "vulPublishedDate": - return ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) - case "vulLastModifiedDate": - return ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) - case "expiryDate": - return ec.fieldContext_TrivyImage_expiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type TrivyImage", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allKubeScores(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allKubeScores(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigResolution(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigResolution(ctx, field) if err != nil { return graphql.Null } @@ -6246,70 +6749,35 @@ func (ec *executionContext) _Query_allKubeScores(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllKubeScores(rctx) + return obj.MisconfigResolution, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.Kubescore) + res := resTmp.(*string) fc.Result = res - return ec.marshalNKubescore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubescoreᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allKubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigResolution(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Kubescore_id(ctx, field) - case "clusterName": - return ec.fieldContext_Kubescore_clusterName(ctx, field) - case "objectName": - return ec.fieldContext_Kubescore_objectName(ctx, field) - case "kind": - return ec.fieldContext_Kubescore_kind(ctx, field) - case "apiVersion": - return ec.fieldContext_Kubescore_apiVersion(ctx, field) - case "name": - return ec.fieldContext_Kubescore_name(ctx, field) - case "namespace": - return ec.fieldContext_Kubescore_namespace(ctx, field) - case "targetType": - return ec.fieldContext_Kubescore_targetType(ctx, field) - case "description": - return ec.fieldContext_Kubescore_description(ctx, field) - case "path": - return ec.fieldContext_Kubescore_path(ctx, field) - case "summary": - return ec.fieldContext_Kubescore_summary(ctx, field) - case "fileName": - return ec.fieldContext_Kubescore_fileName(ctx, field) - case "fileRow": - return ec.fieldContext_Kubescore_fileRow(ctx, field) - case "eventTime": - return ec.fieldContext_Kubescore_eventTime(ctx, field) - case "expiryDate": - return ec.fieldContext_Kubescore_expiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Kubescore", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allTrivyVuls(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allTrivyVuls(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigSeverity(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigSeverity(ctx, field) if err != nil { return graphql.Null } @@ -6322,74 +6790,35 @@ func (ec *executionContext) _Query_allTrivyVuls(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllTrivyVuls(rctx) + return obj.MisconfigSeverity, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.TrivyVul) + res := resTmp.(*string) fc.Result = res - return ec.marshalNTrivyVul2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyVulᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allTrivyVuls(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_TrivyVul_id(ctx, field) - case "clusterName": - return ec.fieldContext_TrivyVul_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_TrivyVul_namespace(ctx, field) - case "kind": - return ec.fieldContext_TrivyVul_kind(ctx, field) - case "name": - return ec.fieldContext_TrivyVul_name(ctx, field) - case "vulId": - return ec.fieldContext_TrivyVul_vulId(ctx, field) - case "vulVendorIds": - return ec.fieldContext_TrivyVul_vulVendorIds(ctx, field) - case "vulPkgId": - return ec.fieldContext_TrivyVul_vulPkgId(ctx, field) - case "vulPkgName": - return ec.fieldContext_TrivyVul_vulPkgName(ctx, field) - case "vulPkgPath": - return ec.fieldContext_TrivyVul_vulPkgPath(ctx, field) - case "vulInstalledVersion": - return ec.fieldContext_TrivyVul_vulInstalledVersion(ctx, field) - case "vulFixedVersion": - return ec.fieldContext_TrivyVul_vulFixedVersion(ctx, field) - case "vulTitle": - return ec.fieldContext_TrivyVul_vulTitle(ctx, field) - case "vulSeverity": - return ec.fieldContext_TrivyVul_vulSeverity(ctx, field) - case "vulPublishedDate": - return ec.fieldContext_TrivyVul_vulPublishedDate(ctx, field) - case "vulLastModifiedDate": - return ec.fieldContext_TrivyVul_vulLastModifiedDate(ctx, field) - case "expiryDate": - return ec.fieldContext_TrivyVul_expiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type TrivyVul", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allTrivyMisconfigs(ctx, field) +func (ec *executionContext) _Misconfiguration_misconfigStatus(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_misconfigStatus(ctx, field) if err != nil { return graphql.Null } @@ -6402,74 +6831,35 @@ func (ec *executionContext) _Query_allTrivyMisconfigs(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllTrivyMisconfigs(rctx) + return obj.MisconfigStatus, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.TrivyMisconfig) + res := resTmp.(*string) fc.Result = res - return ec.marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_misconfigStatus(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_TrivyMisconfig_id(ctx, field) - case "clusterName": - return ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_TrivyMisconfig_namespace(ctx, field) - case "kind": - return ec.fieldContext_TrivyMisconfig_kind(ctx, field) - case "name": - return ec.fieldContext_TrivyMisconfig_name(ctx, field) - case "misconfigId": - return ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) - case "misconfigAvdid": - return ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) - case "misconfigType": - return ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) - case "misconfigTitle": - return ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) - case "misconfigDesc": - return ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) - case "misconfigMsg": - return ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) - case "misconfigQuery": - return ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) - case "misconfigResolution": - return ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) - case "misconfigSeverity": - return ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) - case "misconfigStatus": - return ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) - case "eventTime": - return ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) - case "expiryDate": - return ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type TrivyMisconfig", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_uniqueNamespaces(ctx, field) +func (ec *executionContext) _Misconfiguration_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_eventTime(ctx, field) if err != nil { return graphql.Null } @@ -6482,53 +6872,35 @@ func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().UniqueNamespaces(rctx, fc.Args["clusterName"].(string)) + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.Namespace) + res := resTmp.(*string) fc.Result = res - return ec.marshalNNamespace2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_Namespace_name(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Namespace", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_uniqueNamespaces_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_uniqueClusters(ctx, field) +func (ec *executionContext) _Misconfiguration_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_expiryDate(ctx, field) if err != nil { return graphql.Null } @@ -6541,42 +6913,35 @@ func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().UniqueClusters(rctx) + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.Cluster) + res := resTmp.(*string) fc.Result = res - return ec.marshalNCluster2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Misconfiguration_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Misconfiguration", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "name": - return ec.fieldContext_Cluster_name(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Cluster", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_outdatedImagesByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_outdatedImagesByClusterAndNamespace(ctx, field) +func (ec *executionContext) _Misconfiguration_exportedAt(ctx context.Context, field graphql.CollectedField, obj *model.Misconfiguration) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Misconfiguration_exportedAt(ctx, field) if err != nil { return graphql.Null } @@ -6589,7 +6954,136 @@ func (ec *executionContext) _Query_outdatedImagesByClusterAndNamespace(ctx conte }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().OutdatedImagesByClusterAndNamespace(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + return obj.ExportedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Misconfiguration_exportedAt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Misconfiguration", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Namespace_name(ctx context.Context, field graphql.CollectedField, obj *model.Namespace) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Namespace_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Namespace_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Namespace", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_namespace(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_outdatedImages(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OutdatedImages, nil }) if err != nil { ec.Error(ctx, err) @@ -6606,12 +7100,12 @@ func (ec *executionContext) _Query_outdatedImagesByClusterAndNamespace(ctx conte return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_outdatedImagesByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_NamespaceData_outdatedImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "NamespaceData", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "clusterName": @@ -6634,22 +7128,3164 @@ func (ec *executionContext) fieldContext_Query_outdatedImagesByClusterAndNamespa return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_outdatedImagesByClusterAndNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Query_outdatedImagesCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_outdatedImagesCount(ctx, field) +func (ec *executionContext) _NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_kubeScores(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.KubeScores, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.KubeScore) + fc.Result = res + return ec.marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_kubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_KubeScore_id(ctx, field) + case "clusterName": + return ec.fieldContext_KubeScore_clusterName(ctx, field) + case "objectName": + return ec.fieldContext_KubeScore_objectName(ctx, field) + case "kind": + return ec.fieldContext_KubeScore_kind(ctx, field) + case "apiVersion": + return ec.fieldContext_KubeScore_apiVersion(ctx, field) + case "name": + return ec.fieldContext_KubeScore_name(ctx, field) + case "namespace": + return ec.fieldContext_KubeScore_namespace(ctx, field) + case "targetType": + return ec.fieldContext_KubeScore_targetType(ctx, field) + case "description": + return ec.fieldContext_KubeScore_description(ctx, field) + case "path": + return ec.fieldContext_KubeScore_path(ctx, field) + case "summary": + return ec.fieldContext_KubeScore_summary(ctx, field) + case "fileName": + return ec.fieldContext_KubeScore_fileName(ctx, field) + case "fileRow": + return ec.fieldContext_KubeScore_fileRow(ctx, field) + case "eventTime": + return ec.fieldContext_KubeScore_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type KubeScore", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _NamespaceData_resources(ctx context.Context, field graphql.CollectedField, obj *model.NamespaceData) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NamespaceData_resources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Resources, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Resource) + fc.Result = res + return ec.marshalNResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐResourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NamespaceData_resources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NamespaceData", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_Resource_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_Resource_namespace(ctx, field) + case "kind": + return ec.fieldContext_Resource_kind(ctx, field) + case "resource": + return ec.fieldContext_Resource_resource(ctx, field) + case "age": + return ec.fieldContext_Resource_age(ctx, field) + case "eventTime": + return ec.fieldContext_Resource_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Resource", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_pod(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_pod(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Pod, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_pod(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_currentImage(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CurrentImage, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_currentImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_currentTag(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.CurrentTag, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_currentTag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.LatestVersion, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_latestVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.VersionsBehind, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_versionsBehind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.OutdatedImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OutdatedImage_eventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OutdatedImage_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OutdatedImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allEvents(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allEvents(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllEvents(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Event) + fc.Result = res + return ec.marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allEvents(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_Event_ClusterName(ctx, field) + case "Id": + return ec.fieldContext_Event_Id(ctx, field) + case "EventTime": + return ec.fieldContext_Event_EventTime(ctx, field) + case "OpType": + return ec.fieldContext_Event_OpType(ctx, field) + case "Name": + return ec.fieldContext_Event_Name(ctx, field) + case "Namespace": + return ec.fieldContext_Event_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_Event_Kind(ctx, field) + case "Message": + return ec.fieldContext_Event_Message(ctx, field) + case "Reason": + return ec.fieldContext_Event_Reason(ctx, field) + case "Host": + return ec.fieldContext_Event_Host(ctx, field) + case "Event": + return ec.fieldContext_Event_Event(ctx, field) + case "ImageName": + return ec.fieldContext_Event_ImageName(ctx, field) + case "FirstTime": + return ec.fieldContext_Event_FirstTime(ctx, field) + case "LastTime": + return ec.fieldContext_Event_LastTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_Event_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Event", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allRakkess(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allRakkess(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllRakkess(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Rakkess) + fc.Result = res + return ec.marshalNRakkess2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐRakkessᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allRakkess(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_Rakkess_ClusterName(ctx, field) + case "Name": + return ec.fieldContext_Rakkess_Name(ctx, field) + case "Create": + return ec.fieldContext_Rakkess_Create(ctx, field) + case "Delete": + return ec.fieldContext_Rakkess_Delete(ctx, field) + case "List": + return ec.fieldContext_Rakkess_List(ctx, field) + case "Update": + return ec.fieldContext_Rakkess_Update(ctx, field) + case "EventTime": + return ec.fieldContext_Rakkess_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_Rakkess_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Rakkess", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allDeprecatedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allDeprecatedAPIs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllDeprecatedAPIs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.DeprecatedAPI) + fc.Result = res + return ec.marshalNDeprecatedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPIᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allDeprecatedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) + case "ObjectName": + return ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) + case "Description": + return ec.fieldContext_DeprecatedAPI_Description(ctx, field) + case "Kind": + return ec.fieldContext_DeprecatedAPI_Kind(ctx, field) + case "Deprecated": + return ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) + case "Scope": + return ec.fieldContext_DeprecatedAPI_Scope(ctx, field) + case "EventTime": + return ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeprecatedAPI", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allDeletedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allDeletedAPIs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllDeletedAPIs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.DeletedAPI) + fc.Result = res + return ec.marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allDeletedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_DeletedAPI_ClusterName(ctx, field) + case "ObjectName": + return ec.fieldContext_DeletedAPI_ObjectName(ctx, field) + case "Group": + return ec.fieldContext_DeletedAPI_Group(ctx, field) + case "Kind": + return ec.fieldContext_DeletedAPI_Kind(ctx, field) + case "Version": + return ec.fieldContext_DeletedAPI_Version(ctx, field) + case "Name": + return ec.fieldContext_DeletedAPI_Name(ctx, field) + case "Deleted": + return ec.fieldContext_DeletedAPI_Deleted(ctx, field) + case "Scope": + return ec.fieldContext_DeletedAPI_Scope(ctx, field) + case "EventTime": + return ec.fieldContext_DeletedAPI_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeletedAPI", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allGetAllResources(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allGetAllResources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllGetAllResources(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.GetAllResource) + fc.Result = res + return ec.marshalNGetAllResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allGetAllResources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_GetAllResource_ClusterName(ctx, field) + case "Namespace": + return ec.fieldContext_GetAllResource_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_GetAllResource_Kind(ctx, field) + case "Resource": + return ec.fieldContext_GetAllResource_Resource(ctx, field) + case "Age": + return ec.fieldContext_GetAllResource_Age(ctx, field) + case "EventTime": + return ec.fieldContext_GetAllResource_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GetAllResource", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivySBOMs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivySBOMs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivySBOMs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivySbom) + fc.Result = res + return ec.marshalNTrivySBOM2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbomᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivySBOMs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivySBOM_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivySBOM_clusterName(ctx, field) + case "imageName": + return ec.fieldContext_TrivySBOM_imageName(ctx, field) + case "packageName": + return ec.fieldContext_TrivySBOM_packageName(ctx, field) + case "packageUrl": + return ec.fieldContext_TrivySBOM_packageUrl(ctx, field) + case "bomRef": + return ec.fieldContext_TrivySBOM_bomRef(ctx, field) + case "serialNumber": + return ec.fieldContext_TrivySBOM_serialNumber(ctx, field) + case "version": + return ec.fieldContext_TrivySBOM_version(ctx, field) + case "bomFormat": + return ec.fieldContext_TrivySBOM_bomFormat(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivySBOM_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivySBOM", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivyImages(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivyImages(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivyImages(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyImage) + fc.Result = res + return ec.marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyImage_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyImage_clusterName(ctx, field) + case "artifactName": + return ec.fieldContext_TrivyImage_artifactName(ctx, field) + case "vulId": + return ec.fieldContext_TrivyImage_vulId(ctx, field) + case "vulPkgId": + return ec.fieldContext_TrivyImage_vulPkgId(ctx, field) + case "vulPkgName": + return ec.fieldContext_TrivyImage_vulPkgName(ctx, field) + case "vulInstalledVersion": + return ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) + case "vulFixedVersion": + return ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) + case "vulTitle": + return ec.fieldContext_TrivyImage_vulTitle(ctx, field) + case "vulSeverity": + return ec.fieldContext_TrivyImage_vulSeverity(ctx, field) + case "vulPublishedDate": + return ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) + case "vulLastModifiedDate": + return ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyImage_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyImage", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allKubeScores(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allKubeScores(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllKubeScores(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Kubescore) + fc.Result = res + return ec.marshalNKubescore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubescoreᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allKubeScores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Kubescore_id(ctx, field) + case "clusterName": + return ec.fieldContext_Kubescore_clusterName(ctx, field) + case "objectName": + return ec.fieldContext_Kubescore_objectName(ctx, field) + case "kind": + return ec.fieldContext_Kubescore_kind(ctx, field) + case "apiVersion": + return ec.fieldContext_Kubescore_apiVersion(ctx, field) + case "name": + return ec.fieldContext_Kubescore_name(ctx, field) + case "namespace": + return ec.fieldContext_Kubescore_namespace(ctx, field) + case "targetType": + return ec.fieldContext_Kubescore_targetType(ctx, field) + case "description": + return ec.fieldContext_Kubescore_description(ctx, field) + case "path": + return ec.fieldContext_Kubescore_path(ctx, field) + case "summary": + return ec.fieldContext_Kubescore_summary(ctx, field) + case "fileName": + return ec.fieldContext_Kubescore_fileName(ctx, field) + case "fileRow": + return ec.fieldContext_Kubescore_fileRow(ctx, field) + case "eventTime": + return ec.fieldContext_Kubescore_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Kubescore", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivyVuls(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivyVuls(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivyVuls(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyVul) + fc.Result = res + return ec.marshalNTrivyVul2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyVulᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyVuls(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyVul_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyVul_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_TrivyVul_namespace(ctx, field) + case "kind": + return ec.fieldContext_TrivyVul_kind(ctx, field) + case "name": + return ec.fieldContext_TrivyVul_name(ctx, field) + case "vulId": + return ec.fieldContext_TrivyVul_vulId(ctx, field) + case "vulVendorIds": + return ec.fieldContext_TrivyVul_vulVendorIds(ctx, field) + case "vulPkgId": + return ec.fieldContext_TrivyVul_vulPkgId(ctx, field) + case "vulPkgName": + return ec.fieldContext_TrivyVul_vulPkgName(ctx, field) + case "vulPkgPath": + return ec.fieldContext_TrivyVul_vulPkgPath(ctx, field) + case "vulInstalledVersion": + return ec.fieldContext_TrivyVul_vulInstalledVersion(ctx, field) + case "vulFixedVersion": + return ec.fieldContext_TrivyVul_vulFixedVersion(ctx, field) + case "vulTitle": + return ec.fieldContext_TrivyVul_vulTitle(ctx, field) + case "vulSeverity": + return ec.fieldContext_TrivyVul_vulSeverity(ctx, field) + case "vulPublishedDate": + return ec.fieldContext_TrivyVul_vulPublishedDate(ctx, field) + case "vulLastModifiedDate": + return ec.fieldContext_TrivyVul_vulLastModifiedDate(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyVul_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyVul", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allTrivyMisconfigs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllTrivyMisconfigs(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyMisconfig) + fc.Result = res + return ec.marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allTrivyMisconfigs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyMisconfig_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_TrivyMisconfig_namespace(ctx, field) + case "kind": + return ec.fieldContext_TrivyMisconfig_kind(ctx, field) + case "name": + return ec.fieldContext_TrivyMisconfig_name(ctx, field) + case "misconfigId": + return ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) + case "misconfigAvdid": + return ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) + case "misconfigType": + return ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) + case "misconfigTitle": + return ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) + case "misconfigDesc": + return ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) + case "misconfigMsg": + return ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) + case "misconfigQuery": + return ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) + case "misconfigResolution": + return ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) + case "misconfigSeverity": + return ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) + case "misconfigStatus": + return ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) + case "eventTime": + return ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyMisconfig", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_uniqueNamespaces(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().UniqueNamespaces(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Namespace) + fc.Result = res + return ec.marshalNNamespace2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_uniqueNamespaces(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_Namespace_name(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Namespace", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_uniqueNamespaces_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_uniqueClusters(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().UniqueClusters(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Cluster) + fc.Result = res + return ec.marshalNCluster2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_uniqueClusters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_Cluster_name(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Cluster", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_outdatedImagesByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_outdatedImagesByClusterAndNamespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().OutdatedImagesByClusterAndNamespace(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.OutdatedImage) + fc.Result = res + return ec.marshalNOutdatedImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐOutdatedImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_outdatedImagesByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_OutdatedImage_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_OutdatedImage_namespace(ctx, field) + case "pod": + return ec.fieldContext_OutdatedImage_pod(ctx, field) + case "currentImage": + return ec.fieldContext_OutdatedImage_currentImage(ctx, field) + case "currentTag": + return ec.fieldContext_OutdatedImage_currentTag(ctx, field) + case "latestVersion": + return ec.fieldContext_OutdatedImage_latestVersion(ctx, field) + case "versionsBehind": + return ec.fieldContext_OutdatedImage_versionsBehind(ctx, field) + case "eventTime": + return ec.fieldContext_OutdatedImage_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type OutdatedImage", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_outdatedImagesByClusterAndNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_outdatedImagesCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_outdatedImagesCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().OutdatedImagesCount(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_outdatedImagesCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_outdatedImagesCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterNamespaceOutdatedCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterNamespaceOutdatedCount) + fc.Result = res + return ec.marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) + case "outdatedCount": + return ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceOutdatedCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterDeprecatedAPIsCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterDeprecatedAPIsCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterDeprecatedAPIsCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterAPIsCount) + fc.Result = res + return ec.marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterDeprecatedAPIsCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) + case "count": + return ec.fieldContext_ClusterAPIsCount_count(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterAPIsCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterDeletedAPIsCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterDeletedAPIsCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterDeletedAPIsCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterAPIsCount) + fc.Result = res + return ec.marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterDeletedAPIsCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) + case "count": + return ec.fieldContext_ClusterAPIsCount_count(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterAPIsCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_allClusterNamespaceResourceCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_allClusterNamespaceResourceCounts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AllClusterNamespaceResourceCounts(rctx) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.ClusterNamespaceResourceCount) + fc.Result = res + return ec.marshalNClusterNamespaceResourceCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceResourceCountᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_allClusterNamespaceResourceCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterNamespaceResourceCount_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_ClusterNamespaceResourceCount_namespace(ctx, field) + case "resourceCount": + return ec.fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceResourceCount", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_eventsByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_eventsByClusterAndNamespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().EventsByClusterAndNamespace(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Event) + fc.Result = res + return ec.marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_eventsByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_Event_ClusterName(ctx, field) + case "Id": + return ec.fieldContext_Event_Id(ctx, field) + case "EventTime": + return ec.fieldContext_Event_EventTime(ctx, field) + case "OpType": + return ec.fieldContext_Event_OpType(ctx, field) + case "Name": + return ec.fieldContext_Event_Name(ctx, field) + case "Namespace": + return ec.fieldContext_Event_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_Event_Kind(ctx, field) + case "Message": + return ec.fieldContext_Event_Message(ctx, field) + case "Reason": + return ec.fieldContext_Event_Reason(ctx, field) + case "Host": + return ec.fieldContext_Event_Host(ctx, field) + case "Event": + return ec.fieldContext_Event_Event(ctx, field) + case "ImageName": + return ec.fieldContext_Event_ImageName(ctx, field) + case "FirstTime": + return ec.fieldContext_Event_FirstTime(ctx, field) + case "LastTime": + return ec.fieldContext_Event_LastTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_Event_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Event", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_eventsByClusterAndNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_vulnerabilities(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_vulnerabilities(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Vulnerabilities(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Vulnerability) + fc.Result = res + return ec.marshalNVulnerability2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐVulnerabilityᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_vulnerabilities(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Vulnerability_id(ctx, field) + case "clusterName": + return ec.fieldContext_Vulnerability_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_Vulnerability_namespace(ctx, field) + case "kind": + return ec.fieldContext_Vulnerability_kind(ctx, field) + case "name": + return ec.fieldContext_Vulnerability_name(ctx, field) + case "vulId": + return ec.fieldContext_Vulnerability_vulId(ctx, field) + case "vulVendorIds": + return ec.fieldContext_Vulnerability_vulVendorIds(ctx, field) + case "vulPkgId": + return ec.fieldContext_Vulnerability_vulPkgId(ctx, field) + case "vulPkgName": + return ec.fieldContext_Vulnerability_vulPkgName(ctx, field) + case "vulPkgPath": + return ec.fieldContext_Vulnerability_vulPkgPath(ctx, field) + case "vulInstalledVersion": + return ec.fieldContext_Vulnerability_vulInstalledVersion(ctx, field) + case "vulFixedVersion": + return ec.fieldContext_Vulnerability_vulFixedVersion(ctx, field) + case "vulTitle": + return ec.fieldContext_Vulnerability_vulTitle(ctx, field) + case "vulSeverity": + return ec.fieldContext_Vulnerability_vulSeverity(ctx, field) + case "vulPublishedDate": + return ec.fieldContext_Vulnerability_vulPublishedDate(ctx, field) + case "vulLastModifiedDate": + return ec.fieldContext_Vulnerability_vulLastModifiedDate(ctx, field) + case "expiryDate": + return ec.fieldContext_Vulnerability_expiryDate(ctx, field) + case "exportedAt": + return ec.fieldContext_Vulnerability_exportedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Vulnerability", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_vulnerabilities_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_misconfigurations(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_misconfigurations(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Misconfigurations(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.Misconfiguration) + fc.Result = res + return ec.marshalNMisconfiguration2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐMisconfigurationᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_misconfigurations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Misconfiguration_id(ctx, field) + case "clusterName": + return ec.fieldContext_Misconfiguration_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_Misconfiguration_namespace(ctx, field) + case "kind": + return ec.fieldContext_Misconfiguration_kind(ctx, field) + case "name": + return ec.fieldContext_Misconfiguration_name(ctx, field) + case "misconfigId": + return ec.fieldContext_Misconfiguration_misconfigId(ctx, field) + case "misconfigAvdid": + return ec.fieldContext_Misconfiguration_misconfigAvdid(ctx, field) + case "misconfigType": + return ec.fieldContext_Misconfiguration_misconfigType(ctx, field) + case "misconfigTitle": + return ec.fieldContext_Misconfiguration_misconfigTitle(ctx, field) + case "misconfigDesc": + return ec.fieldContext_Misconfiguration_misconfigDesc(ctx, field) + case "misconfigMsg": + return ec.fieldContext_Misconfiguration_misconfigMsg(ctx, field) + case "misconfigQuery": + return ec.fieldContext_Misconfiguration_misconfigQuery(ctx, field) + case "misconfigResolution": + return ec.fieldContext_Misconfiguration_misconfigResolution(ctx, field) + case "misconfigSeverity": + return ec.fieldContext_Misconfiguration_misconfigSeverity(ctx, field) + case "misconfigStatus": + return ec.fieldContext_Misconfiguration_misconfigStatus(ctx, field) + case "eventTime": + return ec.fieldContext_Misconfiguration_eventTime(ctx, field) + case "expiryDate": + return ec.fieldContext_Misconfiguration_expiryDate(ctx, field) + case "exportedAt": + return ec.fieldContext_Misconfiguration_exportedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Misconfiguration", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_misconfigurations_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_kubescores(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_kubescores(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Kubescores(rctx, fc.Args["clustername"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.KubeScore) + fc.Result = res + return ec.marshalNKubeScore2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐKubeScoreᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_kubescores(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_KubeScore_id(ctx, field) + case "clusterName": + return ec.fieldContext_KubeScore_clusterName(ctx, field) + case "objectName": + return ec.fieldContext_KubeScore_objectName(ctx, field) + case "kind": + return ec.fieldContext_KubeScore_kind(ctx, field) + case "apiVersion": + return ec.fieldContext_KubeScore_apiVersion(ctx, field) + case "name": + return ec.fieldContext_KubeScore_name(ctx, field) + case "namespace": + return ec.fieldContext_KubeScore_namespace(ctx, field) + case "targetType": + return ec.fieldContext_KubeScore_targetType(ctx, field) + case "description": + return ec.fieldContext_KubeScore_description(ctx, field) + case "path": + return ec.fieldContext_KubeScore_path(ctx, field) + case "summary": + return ec.fieldContext_KubeScore_summary(ctx, field) + case "fileName": + return ec.fieldContext_KubeScore_fileName(ctx, field) + case "fileRow": + return ec.fieldContext_KubeScore_fileRow(ctx, field) + case "eventTime": + return ec.fieldContext_KubeScore_eventTime(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type KubeScore", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_kubescores_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_getAllResources(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getAllResources(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetAllResources(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.GetAllResource) + fc.Result = res + return ec.marshalNGetAllResource2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐGetAllResourceᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getAllResources(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_GetAllResource_ClusterName(ctx, field) + case "Namespace": + return ec.fieldContext_GetAllResource_Namespace(ctx, field) + case "Kind": + return ec.fieldContext_GetAllResource_Kind(ctx, field) + case "Resource": + return ec.fieldContext_GetAllResource_Resource(ctx, field) + case "Age": + return ec.fieldContext_GetAllResource_Age(ctx, field) + case "EventTime": + return ec.fieldContext_GetAllResource_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_GetAllResource_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GetAllResource", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_getAllResources_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_trivyImages(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_trivyImages(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TrivyImages(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivyImage) + fc.Result = res + return ec.marshalNTrivyImage2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_trivyImages(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivyImage_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivyImage_clusterName(ctx, field) + case "artifactName": + return ec.fieldContext_TrivyImage_artifactName(ctx, field) + case "vulId": + return ec.fieldContext_TrivyImage_vulId(ctx, field) + case "vulPkgId": + return ec.fieldContext_TrivyImage_vulPkgId(ctx, field) + case "vulPkgName": + return ec.fieldContext_TrivyImage_vulPkgName(ctx, field) + case "vulInstalledVersion": + return ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) + case "vulFixedVersion": + return ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) + case "vulTitle": + return ec.fieldContext_TrivyImage_vulTitle(ctx, field) + case "vulSeverity": + return ec.fieldContext_TrivyImage_vulSeverity(ctx, field) + case "vulPublishedDate": + return ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) + case "vulLastModifiedDate": + return ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivyImage_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyImage", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_trivyImages_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_deprecatedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_deprecatedAPIs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().DeprecatedAPIs(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.DeprecatedAPI) + fc.Result = res + return ec.marshalNDeprecatedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeprecatedAPIᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_deprecatedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_DeprecatedAPI_ClusterName(ctx, field) + case "ObjectName": + return ec.fieldContext_DeprecatedAPI_ObjectName(ctx, field) + case "Description": + return ec.fieldContext_DeprecatedAPI_Description(ctx, field) + case "Kind": + return ec.fieldContext_DeprecatedAPI_Kind(ctx, field) + case "Deprecated": + return ec.fieldContext_DeprecatedAPI_Deprecated(ctx, field) + case "Scope": + return ec.fieldContext_DeprecatedAPI_Scope(ctx, field) + case "EventTime": + return ec.fieldContext_DeprecatedAPI_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_DeprecatedAPI_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeprecatedAPI", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_deprecatedAPIs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_deletedAPIs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_deletedAPIs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().DeletedAPIs(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.DeletedAPI) + fc.Result = res + return ec.marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_deletedAPIs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "ClusterName": + return ec.fieldContext_DeletedAPI_ClusterName(ctx, field) + case "ObjectName": + return ec.fieldContext_DeletedAPI_ObjectName(ctx, field) + case "Group": + return ec.fieldContext_DeletedAPI_Group(ctx, field) + case "Kind": + return ec.fieldContext_DeletedAPI_Kind(ctx, field) + case "Version": + return ec.fieldContext_DeletedAPI_Version(ctx, field) + case "Name": + return ec.fieldContext_DeletedAPI_Name(ctx, field) + case "Deleted": + return ec.fieldContext_DeletedAPI_Deleted(ctx, field) + case "Scope": + return ec.fieldContext_DeletedAPI_Scope(ctx, field) + case "EventTime": + return ec.fieldContext_DeletedAPI_EventTime(ctx, field) + case "ExpiryDate": + return ec.fieldContext_DeletedAPI_ExpiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeletedAPI", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_deletedAPIs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_trivySBOMs(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_trivySBOMs(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TrivySBOMs(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.TrivySbom) + fc.Result = res + return ec.marshalNTrivySBOM2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivySbomᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_trivySBOMs(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrivySBOM_id(ctx, field) + case "clusterName": + return ec.fieldContext_TrivySBOM_clusterName(ctx, field) + case "imageName": + return ec.fieldContext_TrivySBOM_imageName(ctx, field) + case "packageName": + return ec.fieldContext_TrivySBOM_packageName(ctx, field) + case "packageUrl": + return ec.fieldContext_TrivySBOM_packageUrl(ctx, field) + case "bomRef": + return ec.fieldContext_TrivySBOM_bomRef(ctx, field) + case "serialNumber": + return ec.fieldContext_TrivySBOM_serialNumber(ctx, field) + case "version": + return ec.fieldContext_TrivySBOM_version(ctx, field) + case "bomFormat": + return ec.fieldContext_TrivySBOM_bomFormat(ctx, field) + case "expiryDate": + return ec.fieldContext_TrivySBOM_expiryDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivySBOM", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_trivySBOMs_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_trivyVulCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_trivyVulCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TrivyVulCount(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.ClusterNamespaceVulCount) + fc.Result = res + return ec.marshalNClusterNamespaceVulCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceVulCount(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_trivyVulCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterNamespaceVulCount_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_ClusterNamespaceVulCount_namespace(ctx, field) + case "vulCount": + return ec.fieldContext_ClusterNamespaceVulCount_vulCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceVulCount", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_trivyVulCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_trivyMisconfigCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_trivyMisconfigCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TrivyMisconfigCount(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.ClusterNamespaceMisconfigCount) + fc.Result = res + return ec.marshalNClusterNamespaceMisconfigCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceMisconfigCount(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_trivyMisconfigCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterNamespaceMisconfigCount_clusterName(ctx, field) + case "namespace": + return ec.fieldContext_ClusterNamespaceMisconfigCount_namespace(ctx, field) + case "misconfigCount": + return ec.fieldContext_ClusterNamespaceMisconfigCount_misconfigCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceMisconfigCount", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_trivyMisconfigCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_deletedAPICount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_deletedAPICount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().DeletedAPICount(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.ClusterDeletedAPICount) + fc.Result = res + return ec.marshalNClusterDeletedAPICount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterDeletedAPICount(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_deletedAPICount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterDeletedAPICount_clusterName(ctx, field) + case "deletedAPICount": + return ec.fieldContext_ClusterDeletedAPICount_deletedAPICount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterDeletedAPICount", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_deletedAPICount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_trivyImageCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_trivyImageCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().TrivyImageCount(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.TrivyImageCount) + fc.Result = res + return ec.marshalNTrivyImageCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageCount(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_trivyImageCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_TrivyImageCount_clusterName(ctx, field) + case "ImageCount": + return ec.fieldContext_TrivyImageCount_ImageCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrivyImageCount", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_trivyImageCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(fc.Args["name"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + fc.Result = res + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query___schema(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + fc.Result = res + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "description": + return ec.fieldContext___Schema_description(ctx, field) + case "types": + return ec.fieldContext___Schema_types(ctx, field) + case "queryType": + return ec.fieldContext___Schema_queryType(ctx, field) + case "mutationType": + return ec.fieldContext___Schema_mutationType(ctx, field) + case "subscriptionType": + return ec.fieldContext___Schema_subscriptionType(ctx, field) + case "directives": + return ec.fieldContext___Schema_directives(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_ClusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Name(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Create(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Create(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Create, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Create(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Delete(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Delete(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Delete, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Delete(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_List(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_List(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.List, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_List(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_Update(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_Update(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Update, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_Update(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_EventTime(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EventTime, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Rakkess_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Rakkess_ExpiryDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ExpiryDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Rakkess_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Rakkess", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_namespace(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Namespace, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Resource_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Resource", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_kind(ctx, field) if err != nil { return graphql.Null } @@ -6662,7 +10298,7 @@ func (ec *executionContext) _Query_outdatedImagesCount(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().OutdatedImagesCount(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -6674,37 +10310,26 @@ func (ec *executionContext) _Query_outdatedImagesCount(ctx context.Context, fiel } return graphql.Null } - res := resTmp.(int) + res := resTmp.(string) fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_outdatedImagesCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Resource_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Resource", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_outdatedImagesCount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx, field) +func (ec *executionContext) _Resource_resource(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_resource(ctx, field) if err != nil { return graphql.Null } @@ -6717,7 +10342,7 @@ func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllClusterNamespaceOutdatedCounts(rctx) + return obj.Resource, nil }) if err != nil { ec.Error(ctx, err) @@ -6729,34 +10354,26 @@ func (ec *executionContext) _Query_allClusterNamespaceOutdatedCounts(ctx context } return graphql.Null } - res := resTmp.([]*model.ClusterNamespaceOutdatedCount) + res := resTmp.(string) fc.Result = res - return ec.marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allClusterNamespaceOutdatedCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Resource_resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Resource", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_ClusterNamespaceOutdatedCount_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_ClusterNamespaceOutdatedCount_namespace(ctx, field) - case "outdatedCount": - return ec.fieldContext_ClusterNamespaceOutdatedCount_outdatedCount(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceOutdatedCount", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allClusterDeprecatedAPIsCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allClusterDeprecatedAPIsCounts(ctx, field) +func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_age(ctx, field) if err != nil { return graphql.Null } @@ -6769,7 +10386,7 @@ func (ec *executionContext) _Query_allClusterDeprecatedAPIsCounts(ctx context.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllClusterDeprecatedAPIsCounts(rctx) + return obj.Age, nil }) if err != nil { ec.Error(ctx, err) @@ -6781,32 +10398,26 @@ func (ec *executionContext) _Query_allClusterDeprecatedAPIsCounts(ctx context.Co } return graphql.Null } - res := resTmp.([]*model.ClusterAPIsCount) + res := resTmp.(string) fc.Result = res - return ec.marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allClusterDeprecatedAPIsCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Resource", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) - case "count": - return ec.fieldContext_ClusterAPIsCount_count(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ClusterAPIsCount", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allClusterDeletedAPIsCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allClusterDeletedAPIsCounts(ctx, field) +func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Resource_eventTime(ctx, field) if err != nil { return graphql.Null } @@ -6819,7 +10430,7 @@ func (ec *executionContext) _Query_allClusterDeletedAPIsCounts(ctx context.Conte }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllClusterDeletedAPIsCounts(rctx) + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -6831,32 +10442,26 @@ func (ec *executionContext) _Query_allClusterDeletedAPIsCounts(ctx context.Conte } return graphql.Null } - res := resTmp.([]*model.ClusterAPIsCount) + res := resTmp.(string) fc.Result = res - return ec.marshalNClusterAPIsCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterAPIsCountᚄ(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allClusterDeletedAPIsCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Resource", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_ClusterAPIsCount_clusterName(ctx, field) - case "count": - return ec.fieldContext_ClusterAPIsCount_count(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ClusterAPIsCount", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_allClusterNamespaceResourceCounts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allClusterNamespaceResourceCounts(ctx, field) +func (ec *executionContext) _TrivyImage_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_id(ctx, field) if err != nil { return graphql.Null } @@ -6869,7 +10474,7 @@ func (ec *executionContext) _Query_allClusterNamespaceResourceCounts(ctx context }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllClusterNamespaceResourceCounts(rctx) + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -6881,34 +10486,26 @@ func (ec *executionContext) _Query_allClusterNamespaceResourceCounts(ctx context } return graphql.Null } - res := resTmp.([]*model.ClusterNamespaceResourceCount) + res := resTmp.(string) fc.Result = res - return ec.marshalNClusterNamespaceResourceCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceResourceCountᚄ(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allClusterNamespaceResourceCounts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "TrivyImage", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "clusterName": - return ec.fieldContext_ClusterNamespaceResourceCount_clusterName(ctx, field) - case "namespace": - return ec.fieldContext_ClusterNamespaceResourceCount_namespace(ctx, field) - case "resourceCount": - return ec.fieldContext_ClusterNamespaceResourceCount_resourceCount(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ClusterNamespaceResourceCount", field.Name) + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Query_eventsByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_eventsByClusterAndNamespace(ctx, field) +func (ec *executionContext) _TrivyImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -6921,81 +10518,76 @@ func (ec *executionContext) _Query_eventsByClusterAndNamespace(ctx context.Conte }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().EventsByClusterAndNamespace(rctx, fc.Args["clusterName"].(string), fc.Args["namespace"].(string)) + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.([]*model.Event) + res := resTmp.(*string) fc.Result = res - return ec.marshalNEvent2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐEventᚄ(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_eventsByClusterAndNamespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "TrivyImage", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "ClusterName": - return ec.fieldContext_Event_ClusterName(ctx, field) - case "Id": - return ec.fieldContext_Event_Id(ctx, field) - case "EventTime": - return ec.fieldContext_Event_EventTime(ctx, field) - case "OpType": - return ec.fieldContext_Event_OpType(ctx, field) - case "Name": - return ec.fieldContext_Event_Name(ctx, field) - case "Namespace": - return ec.fieldContext_Event_Namespace(ctx, field) - case "Kind": - return ec.fieldContext_Event_Kind(ctx, field) - case "Message": - return ec.fieldContext_Event_Message(ctx, field) - case "Reason": - return ec.fieldContext_Event_Reason(ctx, field) - case "Host": - return ec.fieldContext_Event_Host(ctx, field) - case "Event": - return ec.fieldContext_Event_Event(ctx, field) - case "ImageName": - return ec.fieldContext_Event_ImageName(ctx, field) - case "FirstTime": - return ec.fieldContext_Event_FirstTime(ctx, field) - case "LastTime": - return ec.fieldContext_Event_LastTime(ctx, field) - case "ExpiryDate": - return ec.fieldContext_Event_ExpiryDate(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Event", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } + return fc, nil +} + +func (ec *executionContext) _TrivyImage_artifactName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_artifactName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) defer func() { if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null } }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_eventsByClusterAndNamespace_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ArtifactName, nil + }) + if err != nil { ec.Error(ctx, err) - return fc, err + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivyImage_artifactName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivyImage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, } return fc, nil } -func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___type(ctx, field) +func (ec *executionContext) _TrivyImage_vulId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulId(ctx, field) if err != nil { return graphql.Null } @@ -7008,7 +10600,7 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.introspectType(fc.Args["name"].(string)) + return obj.VulID, nil }) if err != nil { ec.Error(ctx, err) @@ -7017,59 +10609,26 @@ func (ec *executionContext) _Query___type(ctx context.Context, field graphql.Col if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Type) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "TrivyImage", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "kind": - return ec.fieldContext___Type_kind(ctx, field) - case "name": - return ec.fieldContext___Type_name(ctx, field) - case "description": - return ec.fieldContext___Type_description(ctx, field) - case "fields": - return ec.fieldContext___Type_fields(ctx, field) - case "interfaces": - return ec.fieldContext___Type_interfaces(ctx, field) - case "possibleTypes": - return ec.fieldContext___Type_possibleTypes(ctx, field) - case "enumValues": - return ec.fieldContext___Type_enumValues(ctx, field) - case "inputFields": - return ec.fieldContext___Type_inputFields(ctx, field) - case "ofType": - return ec.fieldContext___Type_ofType(ctx, field) - case "specifiedByURL": - return ec.fieldContext___Type_specifiedByURL(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query___schema(ctx, field) +func (ec *executionContext) _TrivyImage_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulPkgId(ctx, field) if err != nil { return graphql.Null } @@ -7082,7 +10641,7 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.introspectSchema() + return obj.VulPkgID, nil }) if err != nil { ec.Error(ctx, err) @@ -7091,40 +10650,26 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(*introspection.Schema) + res := resTmp.(*string) fc.Result = res - return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "TrivyImage", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "description": - return ec.fieldContext___Schema_description(ctx, field) - case "types": - return ec.fieldContext___Schema_types(ctx, field) - case "queryType": - return ec.fieldContext___Schema_queryType(ctx, field) - case "mutationType": - return ec.fieldContext___Schema_mutationType(ctx, field) - case "subscriptionType": - return ec.fieldContext___Schema_subscriptionType(ctx, field) - case "directives": - return ec.fieldContext___Schema_directives(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Rakkess_ClusterName(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_ClusterName(ctx, field) +func (ec *executionContext) _TrivyImage_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulPkgName(ctx, field) if err != nil { return graphql.Null } @@ -7137,7 +10682,7 @@ func (ec *executionContext) _Rakkess_ClusterName(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.VulPkgName, nil }) if err != nil { ec.Error(ctx, err) @@ -7151,9 +10696,9 @@ func (ec *executionContext) _Rakkess_ClusterName(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_ClusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7164,8 +10709,8 @@ func (ec *executionContext) fieldContext_Rakkess_ClusterName(ctx context.Context return fc, nil } -func (ec *executionContext) _Rakkess_Name(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_Name(ctx, field) +func (ec *executionContext) _TrivyImage_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) if err != nil { return graphql.Null } @@ -7178,7 +10723,7 @@ func (ec *executionContext) _Rakkess_Name(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.VulInstalledVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -7192,9 +10737,9 @@ func (ec *executionContext) _Rakkess_Name(ctx context.Context, field graphql.Col return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7205,8 +10750,8 @@ func (ec *executionContext) fieldContext_Rakkess_Name(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Rakkess_Create(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_Create(ctx, field) +func (ec *executionContext) _TrivyImage_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) if err != nil { return graphql.Null } @@ -7219,7 +10764,7 @@ func (ec *executionContext) _Rakkess_Create(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Create, nil + return obj.VulFixedVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -7233,9 +10778,9 @@ func (ec *executionContext) _Rakkess_Create(ctx context.Context, field graphql.C return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_Create(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7246,8 +10791,8 @@ func (ec *executionContext) fieldContext_Rakkess_Create(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Rakkess_Delete(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_Delete(ctx, field) +func (ec *executionContext) _TrivyImage_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulTitle(ctx, field) if err != nil { return graphql.Null } @@ -7260,7 +10805,7 @@ func (ec *executionContext) _Rakkess_Delete(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Delete, nil + return obj.VulTitle, nil }) if err != nil { ec.Error(ctx, err) @@ -7274,9 +10819,9 @@ func (ec *executionContext) _Rakkess_Delete(ctx context.Context, field graphql.C return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_Delete(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7287,8 +10832,8 @@ func (ec *executionContext) fieldContext_Rakkess_Delete(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Rakkess_List(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_List(ctx, field) +func (ec *executionContext) _TrivyImage_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulSeverity(ctx, field) if err != nil { return graphql.Null } @@ -7301,7 +10846,7 @@ func (ec *executionContext) _Rakkess_List(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.List, nil + return obj.VulSeverity, nil }) if err != nil { ec.Error(ctx, err) @@ -7315,9 +10860,9 @@ func (ec *executionContext) _Rakkess_List(ctx context.Context, field graphql.Col return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_List(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7328,8 +10873,8 @@ func (ec *executionContext) fieldContext_Rakkess_List(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Rakkess_Update(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_Update(ctx, field) +func (ec *executionContext) _TrivyImage_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) if err != nil { return graphql.Null } @@ -7342,7 +10887,7 @@ func (ec *executionContext) _Rakkess_Update(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Update, nil + return obj.VulPublishedDate, nil }) if err != nil { ec.Error(ctx, err) @@ -7356,9 +10901,9 @@ func (ec *executionContext) _Rakkess_Update(ctx context.Context, field graphql.C return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_Update(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7369,8 +10914,8 @@ func (ec *executionContext) fieldContext_Rakkess_Update(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _Rakkess_EventTime(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_EventTime(ctx, field) +func (ec *executionContext) _TrivyImage_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) if err != nil { return graphql.Null } @@ -7383,7 +10928,7 @@ func (ec *executionContext) _Rakkess_EventTime(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.VulLastModifiedDate, nil }) if err != nil { ec.Error(ctx, err) @@ -7397,9 +10942,9 @@ func (ec *executionContext) _Rakkess_EventTime(ctx context.Context, field graphq return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_EventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7410,8 +10955,8 @@ func (ec *executionContext) fieldContext_Rakkess_EventTime(ctx context.Context, return fc, nil } -func (ec *executionContext) _Rakkess_ExpiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Rakkess) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Rakkess_ExpiryDate(ctx, field) +func (ec *executionContext) _TrivyImage_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImage_expiryDate(ctx, field) if err != nil { return graphql.Null } @@ -7438,9 +10983,9 @@ func (ec *executionContext) _Rakkess_ExpiryDate(ctx context.Context, field graph return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Rakkess_ExpiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImage_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Rakkess", + Object: "TrivyImage", Field: field, IsMethod: false, IsResolver: false, @@ -7451,8 +10996,8 @@ func (ec *executionContext) fieldContext_Rakkess_ExpiryDate(ctx context.Context, return fc, nil } -func (ec *executionContext) _Resource_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_clusterName(ctx, field) +func (ec *executionContext) _TrivyImageCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImageCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImageCount_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -7482,9 +11027,9 @@ func (ec *executionContext) _Resource_clusterName(ctx context.Context, field gra return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImageCount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "TrivyImageCount", Field: field, IsMethod: false, IsResolver: false, @@ -7495,8 +11040,8 @@ func (ec *executionContext) fieldContext_Resource_clusterName(ctx context.Contex return fc, nil } -func (ec *executionContext) _Resource_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_namespace(ctx, field) +func (ec *executionContext) _TrivyImageCount_ImageCount(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImageCount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyImageCount_ImageCount(ctx, field) if err != nil { return graphql.Null } @@ -7509,7 +11054,7 @@ func (ec *executionContext) _Resource_namespace(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.ImageCount, nil }) if err != nil { ec.Error(ctx, err) @@ -7521,26 +11066,26 @@ func (ec *executionContext) _Resource_namespace(ctx context.Context, field graph } return graphql.Null } - res := resTmp.(string) + res := resTmp.(int) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyImageCount_ImageCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "TrivyImageCount", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_kind(ctx, field) +func (ec *executionContext) _TrivyMisconfig_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_id(ctx, field) if err != nil { return graphql.Null } @@ -7553,7 +11098,7 @@ func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -7567,24 +11112,24 @@ func (ec *executionContext) _Resource_kind(ctx context.Context, field graphql.Co } res := resTmp.(string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Resource_resource(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_resource(ctx, field) +func (ec *executionContext) _TrivyMisconfig_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -7597,26 +11142,23 @@ func (ec *executionContext) _Resource_resource(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Resource, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_resource(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7627,8 +11169,8 @@ func (ec *executionContext) fieldContext_Resource_resource(ctx context.Context, return fc, nil } -func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_age(ctx, field) +func (ec *executionContext) _TrivyMisconfig_namespace(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_namespace(ctx, field) if err != nil { return graphql.Null } @@ -7641,26 +11183,23 @@ func (ec *executionContext) _Resource_age(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Age, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7671,8 +11210,8 @@ func (ec *executionContext) fieldContext_Resource_age(ctx context.Context, field return fc, nil } -func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.Resource) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Resource_eventTime(ctx, field) +func (ec *executionContext) _TrivyMisconfig_kind(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_kind(ctx, field) if err != nil { return graphql.Null } @@ -7685,26 +11224,23 @@ func (ec *executionContext) _Resource_eventTime(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Resource", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7715,8 +11251,8 @@ func (ec *executionContext) fieldContext_Resource_eventTime(ctx context.Context, return fc, nil } -func (ec *executionContext) _TrivyImage_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_id(ctx, field) +func (ec *executionContext) _TrivyMisconfig_name(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_name(ctx, field) if err != nil { return graphql.Null } @@ -7729,38 +11265,35 @@ func (ec *executionContext) _TrivyImage_id(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TrivyImage_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_clusterName(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) if err != nil { return graphql.Null } @@ -7773,7 +11306,7 @@ func (ec *executionContext) _TrivyImage_clusterName(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.MisconfigID, nil }) if err != nil { ec.Error(ctx, err) @@ -7787,9 +11320,9 @@ func (ec *executionContext) _TrivyImage_clusterName(ctx context.Context, field g return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7800,8 +11333,8 @@ func (ec *executionContext) fieldContext_TrivyImage_clusterName(ctx context.Cont return fc, nil } -func (ec *executionContext) _TrivyImage_artifactName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_artifactName(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigAvdid(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) if err != nil { return graphql.Null } @@ -7814,7 +11347,7 @@ func (ec *executionContext) _TrivyImage_artifactName(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ArtifactName, nil + return obj.MisconfigAvdid, nil }) if err != nil { ec.Error(ctx, err) @@ -7828,9 +11361,9 @@ func (ec *executionContext) _TrivyImage_artifactName(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_artifactName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigAvdid(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7841,8 +11374,8 @@ func (ec *executionContext) fieldContext_TrivyImage_artifactName(ctx context.Con return fc, nil } -func (ec *executionContext) _TrivyImage_vulId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulId(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigType(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) if err != nil { return graphql.Null } @@ -7855,7 +11388,7 @@ func (ec *executionContext) _TrivyImage_vulId(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulID, nil + return obj.MisconfigType, nil }) if err != nil { ec.Error(ctx, err) @@ -7869,9 +11402,9 @@ func (ec *executionContext) _TrivyImage_vulId(ctx context.Context, field graphql return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7882,8 +11415,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulId(ctx context.Context, f return fc, nil } -func (ec *executionContext) _TrivyImage_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulPkgId(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) if err != nil { return graphql.Null } @@ -7896,7 +11429,7 @@ func (ec *executionContext) _TrivyImage_vulPkgId(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPkgID, nil + return obj.MisconfigTitle, nil }) if err != nil { ec.Error(ctx, err) @@ -7910,9 +11443,9 @@ func (ec *executionContext) _TrivyImage_vulPkgId(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7923,8 +11456,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulPkgId(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivyImage_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulPkgName(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigDesc(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) if err != nil { return graphql.Null } @@ -7937,7 +11470,7 @@ func (ec *executionContext) _TrivyImage_vulPkgName(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPkgName, nil + return obj.MisconfigDesc, nil }) if err != nil { ec.Error(ctx, err) @@ -7951,9 +11484,9 @@ func (ec *executionContext) _TrivyImage_vulPkgName(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigDesc(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -7964,8 +11497,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulPkgName(ctx context.Conte return fc, nil } -func (ec *executionContext) _TrivyImage_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulInstalledVersion(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigMsg(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) if err != nil { return graphql.Null } @@ -7978,7 +11511,7 @@ func (ec *executionContext) _TrivyImage_vulInstalledVersion(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulInstalledVersion, nil + return obj.MisconfigMsg, nil }) if err != nil { ec.Error(ctx, err) @@ -7992,9 +11525,9 @@ func (ec *executionContext) _TrivyImage_vulInstalledVersion(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigMsg(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8005,8 +11538,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulInstalledVersion(ctx cont return fc, nil } -func (ec *executionContext) _TrivyImage_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulFixedVersion(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigQuery(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) if err != nil { return graphql.Null } @@ -8019,7 +11552,7 @@ func (ec *executionContext) _TrivyImage_vulFixedVersion(ctx context.Context, fie }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulFixedVersion, nil + return obj.MisconfigQuery, nil }) if err != nil { ec.Error(ctx, err) @@ -8033,9 +11566,9 @@ func (ec *executionContext) _TrivyImage_vulFixedVersion(ctx context.Context, fie return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigQuery(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8046,8 +11579,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulFixedVersion(ctx context. return fc, nil } -func (ec *executionContext) _TrivyImage_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulTitle(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigResolution(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) if err != nil { return graphql.Null } @@ -8060,7 +11593,7 @@ func (ec *executionContext) _TrivyImage_vulTitle(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulTitle, nil + return obj.MisconfigResolution, nil }) if err != nil { ec.Error(ctx, err) @@ -8074,9 +11607,9 @@ func (ec *executionContext) _TrivyImage_vulTitle(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigResolution(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8087,8 +11620,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulTitle(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivyImage_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulSeverity(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) if err != nil { return graphql.Null } @@ -8101,7 +11634,7 @@ func (ec *executionContext) _TrivyImage_vulSeverity(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulSeverity, nil + return obj.MisconfigSeverity, nil }) if err != nil { ec.Error(ctx, err) @@ -8115,9 +11648,9 @@ func (ec *executionContext) _TrivyImage_vulSeverity(ctx context.Context, field g return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8128,8 +11661,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulSeverity(ctx context.Cont return fc, nil } -func (ec *executionContext) _TrivyImage_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulPublishedDate(ctx, field) +func (ec *executionContext) _TrivyMisconfig_misconfigStatus(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) if err != nil { return graphql.Null } @@ -8142,7 +11675,7 @@ func (ec *executionContext) _TrivyImage_vulPublishedDate(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPublishedDate, nil + return obj.MisconfigStatus, nil }) if err != nil { ec.Error(ctx, err) @@ -8156,9 +11689,9 @@ func (ec *executionContext) _TrivyImage_vulPublishedDate(ctx context.Context, fi return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigStatus(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8169,8 +11702,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulPublishedDate(ctx context return fc, nil } -func (ec *executionContext) _TrivyImage_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_vulLastModifiedDate(ctx, field) +func (ec *executionContext) _TrivyMisconfig_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) if err != nil { return graphql.Null } @@ -8183,7 +11716,7 @@ func (ec *executionContext) _TrivyImage_vulLastModifiedDate(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulLastModifiedDate, nil + return obj.EventTime, nil }) if err != nil { ec.Error(ctx, err) @@ -8197,9 +11730,9 @@ func (ec *executionContext) _TrivyImage_vulLastModifiedDate(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8210,8 +11743,8 @@ func (ec *executionContext) fieldContext_TrivyImage_vulLastModifiedDate(ctx cont return fc, nil } -func (ec *executionContext) _TrivyImage_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyImage) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyImage_expiryDate(ctx, field) +func (ec *executionContext) _TrivyMisconfig_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) if err != nil { return graphql.Null } @@ -8238,9 +11771,9 @@ func (ec *executionContext) _TrivyImage_expiryDate(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyImage_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyMisconfig_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyImage", + Object: "TrivyMisconfig", Field: field, IsMethod: false, IsResolver: false, @@ -8251,8 +11784,8 @@ func (ec *executionContext) fieldContext_TrivyImage_expiryDate(ctx context.Conte return fc, nil } -func (ec *executionContext) _TrivyMisconfig_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_id(ctx, field) +func (ec *executionContext) _TrivySBOM_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_id(ctx, field) if err != nil { return graphql.Null } @@ -8282,9 +11815,9 @@ func (ec *executionContext) _TrivyMisconfig_id(ctx context.Context, field graphq return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8295,8 +11828,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_id(ctx context.Context, return fc, nil } -func (ec *executionContext) _TrivyMisconfig_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_clusterName(ctx, field) +func (ec *executionContext) _TrivySBOM_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -8323,9 +11856,9 @@ func (ec *executionContext) _TrivyMisconfig_clusterName(ctx context.Context, fie return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8336,8 +11869,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_clusterName(ctx context. return fc, nil } -func (ec *executionContext) _TrivyMisconfig_namespace(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_namespace(ctx, field) +func (ec *executionContext) _TrivySBOM_imageName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_imageName(ctx, field) if err != nil { return graphql.Null } @@ -8350,7 +11883,7 @@ func (ec *executionContext) _TrivyMisconfig_namespace(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.ImageName, nil }) if err != nil { ec.Error(ctx, err) @@ -8364,9 +11897,9 @@ func (ec *executionContext) _TrivyMisconfig_namespace(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_imageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8377,8 +11910,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_namespace(ctx context.Co return fc, nil } -func (ec *executionContext) _TrivyMisconfig_kind(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_kind(ctx, field) +func (ec *executionContext) _TrivySBOM_packageName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_packageName(ctx, field) if err != nil { return graphql.Null } @@ -8391,7 +11924,7 @@ func (ec *executionContext) _TrivyMisconfig_kind(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.PackageName, nil }) if err != nil { ec.Error(ctx, err) @@ -8405,9 +11938,9 @@ func (ec *executionContext) _TrivyMisconfig_kind(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_packageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8418,8 +11951,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_kind(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivyMisconfig_name(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_name(ctx, field) +func (ec *executionContext) _TrivySBOM_packageUrl(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_packageUrl(ctx, field) if err != nil { return graphql.Null } @@ -8432,7 +11965,7 @@ func (ec *executionContext) _TrivyMisconfig_name(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.PackageURL, nil }) if err != nil { ec.Error(ctx, err) @@ -8446,9 +11979,9 @@ func (ec *executionContext) _TrivyMisconfig_name(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_packageUrl(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8459,8 +11992,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_name(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigId(ctx, field) +func (ec *executionContext) _TrivySBOM_bomRef(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_bomRef(ctx, field) if err != nil { return graphql.Null } @@ -8473,7 +12006,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigId(ctx context.Context, fie }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigID, nil + return obj.BomRef, nil }) if err != nil { ec.Error(ctx, err) @@ -8487,9 +12020,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigId(ctx context.Context, fie return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_bomRef(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8500,8 +12033,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigId(ctx context. return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigAvdid(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigAvdid(ctx, field) +func (ec *executionContext) _TrivySBOM_serialNumber(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_serialNumber(ctx, field) if err != nil { return graphql.Null } @@ -8514,7 +12047,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigAvdid(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigAvdid, nil + return obj.SerialNumber, nil }) if err != nil { ec.Error(ctx, err) @@ -8528,9 +12061,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigAvdid(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigAvdid(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_serialNumber(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8541,8 +12074,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigAvdid(ctx conte return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigType(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigType(ctx, field) +func (ec *executionContext) _TrivySBOM_version(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_version(ctx, field) if err != nil { return graphql.Null } @@ -8555,7 +12088,48 @@ func (ec *executionContext) _TrivyMisconfig_misconfigType(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigType, nil + return obj.Version, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_TrivySBOM_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TrivySBOM", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _TrivySBOM_bomFormat(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_bomFormat(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.BomFormat, nil }) if err != nil { ec.Error(ctx, err) @@ -8569,9 +12143,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigType(ctx context.Context, f return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_bomFormat(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8582,8 +12156,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigType(ctx contex return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigTitle(ctx, field) +func (ec *executionContext) _TrivySBOM_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivySBOM_expiryDate(ctx, field) if err != nil { return graphql.Null } @@ -8596,7 +12170,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigTitle(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigTitle, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) @@ -8610,9 +12184,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigTitle(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivySBOM_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivySBOM", Field: field, IsMethod: false, IsResolver: false, @@ -8623,8 +12197,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigTitle(ctx conte return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigDesc(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigDesc(ctx, field) +func (ec *executionContext) _TrivyVul_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_id(ctx, field) if err != nil { return graphql.Null } @@ -8637,35 +12211,38 @@ func (ec *executionContext) _TrivyMisconfig_misconfigDesc(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigDesc, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigDesc(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigMsg(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigMsg(ctx, field) +func (ec *executionContext) _TrivyVul_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -8678,7 +12255,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigMsg(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigMsg, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -8692,9 +12269,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigMsg(ctx context.Context, fi return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigMsg(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8705,8 +12282,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigMsg(ctx context return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigQuery(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigQuery(ctx, field) +func (ec *executionContext) _TrivyVul_namespace(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_namespace(ctx, field) if err != nil { return graphql.Null } @@ -8719,7 +12296,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigQuery(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigQuery, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) @@ -8733,9 +12310,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigQuery(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigQuery(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8746,8 +12323,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigQuery(ctx conte return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigResolution(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigResolution(ctx, field) +func (ec *executionContext) _TrivyVul_kind(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_kind(ctx, field) if err != nil { return graphql.Null } @@ -8760,7 +12337,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigResolution(ctx context.Cont }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigResolution, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) @@ -8774,9 +12351,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigResolution(ctx context.Cont return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigResolution(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8787,8 +12364,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigResolution(ctx return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigSeverity(ctx, field) +func (ec *executionContext) _TrivyVul_name(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_name(ctx, field) if err != nil { return graphql.Null } @@ -8801,7 +12378,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigSeverity(ctx context.Contex }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigSeverity, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -8815,9 +12392,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigSeverity(ctx context.Contex return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8828,8 +12405,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigSeverity(ctx co return fc, nil } -func (ec *executionContext) _TrivyMisconfig_misconfigStatus(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_misconfigStatus(ctx, field) +func (ec *executionContext) _TrivyVul_vulId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulId(ctx, field) if err != nil { return graphql.Null } @@ -8842,7 +12419,7 @@ func (ec *executionContext) _TrivyMisconfig_misconfigStatus(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.MisconfigStatus, nil + return obj.VulID, nil }) if err != nil { ec.Error(ctx, err) @@ -8856,9 +12433,9 @@ func (ec *executionContext) _TrivyMisconfig_misconfigStatus(ctx context.Context, return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigStatus(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8869,8 +12446,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_misconfigStatus(ctx cont return fc, nil } -func (ec *executionContext) _TrivyMisconfig_eventTime(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_eventTime(ctx, field) +func (ec *executionContext) _TrivyVul_vulVendorIds(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulVendorIds(ctx, field) if err != nil { return graphql.Null } @@ -8883,7 +12460,7 @@ func (ec *executionContext) _TrivyMisconfig_eventTime(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.EventTime, nil + return obj.VulVendorIds, nil }) if err != nil { ec.Error(ctx, err) @@ -8897,9 +12474,9 @@ func (ec *executionContext) _TrivyMisconfig_eventTime(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_eventTime(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulVendorIds(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8910,8 +12487,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_eventTime(ctx context.Co return fc, nil } -func (ec *executionContext) _TrivyMisconfig_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyMisconfig) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyMisconfig_expiryDate(ctx, field) +func (ec *executionContext) _TrivyVul_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPkgId(ctx, field) if err != nil { return graphql.Null } @@ -8924,7 +12501,7 @@ func (ec *executionContext) _TrivyMisconfig_expiryDate(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.VulPkgID, nil }) if err != nil { ec.Error(ctx, err) @@ -8938,9 +12515,9 @@ func (ec *executionContext) _TrivyMisconfig_expiryDate(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyMisconfig_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyMisconfig", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -8951,8 +12528,8 @@ func (ec *executionContext) fieldContext_TrivyMisconfig_expiryDate(ctx context.C return fc, nil } -func (ec *executionContext) _TrivySBOM_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_id(ctx, field) +func (ec *executionContext) _TrivyVul_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPkgName(ctx, field) if err != nil { return graphql.Null } @@ -8965,38 +12542,35 @@ func (ec *executionContext) _TrivySBOM_id(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return obj.VulPkgName, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TrivySBOM_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_clusterName(ctx, field) +func (ec *executionContext) _TrivyVul_vulPkgPath(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPkgPath(ctx, field) if err != nil { return graphql.Null } @@ -9009,7 +12583,7 @@ func (ec *executionContext) _TrivySBOM_clusterName(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.VulPkgPath, nil }) if err != nil { ec.Error(ctx, err) @@ -9023,9 +12597,9 @@ func (ec *executionContext) _TrivySBOM_clusterName(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulPkgPath(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9036,8 +12610,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_clusterName(ctx context.Conte return fc, nil } -func (ec *executionContext) _TrivySBOM_imageName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_imageName(ctx, field) +func (ec *executionContext) _TrivyVul_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulInstalledVersion(ctx, field) if err != nil { return graphql.Null } @@ -9050,7 +12624,7 @@ func (ec *executionContext) _TrivySBOM_imageName(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ImageName, nil + return obj.VulInstalledVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -9064,9 +12638,9 @@ func (ec *executionContext) _TrivySBOM_imageName(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_imageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9077,8 +12651,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_imageName(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivySBOM_packageName(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_packageName(ctx, field) +func (ec *executionContext) _TrivyVul_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulFixedVersion(ctx, field) if err != nil { return graphql.Null } @@ -9091,7 +12665,7 @@ func (ec *executionContext) _TrivySBOM_packageName(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.PackageName, nil + return obj.VulFixedVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -9105,9 +12679,9 @@ func (ec *executionContext) _TrivySBOM_packageName(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_packageName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9118,8 +12692,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_packageName(ctx context.Conte return fc, nil } -func (ec *executionContext) _TrivySBOM_packageUrl(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_packageUrl(ctx, field) +func (ec *executionContext) _TrivyVul_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulTitle(ctx, field) if err != nil { return graphql.Null } @@ -9132,7 +12706,7 @@ func (ec *executionContext) _TrivySBOM_packageUrl(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.PackageURL, nil + return obj.VulTitle, nil }) if err != nil { ec.Error(ctx, err) @@ -9146,9 +12720,9 @@ func (ec *executionContext) _TrivySBOM_packageUrl(ctx context.Context, field gra return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_packageUrl(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9159,8 +12733,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_packageUrl(ctx context.Contex return fc, nil } -func (ec *executionContext) _TrivySBOM_bomRef(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_bomRef(ctx, field) +func (ec *executionContext) _TrivyVul_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulSeverity(ctx, field) if err != nil { return graphql.Null } @@ -9173,7 +12747,7 @@ func (ec *executionContext) _TrivySBOM_bomRef(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.BomRef, nil + return obj.VulSeverity, nil }) if err != nil { ec.Error(ctx, err) @@ -9187,9 +12761,9 @@ func (ec *executionContext) _TrivySBOM_bomRef(ctx context.Context, field graphql return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_bomRef(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9200,8 +12774,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_bomRef(ctx context.Context, f return fc, nil } -func (ec *executionContext) _TrivySBOM_serialNumber(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_serialNumber(ctx, field) +func (ec *executionContext) _TrivyVul_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulPublishedDate(ctx, field) if err != nil { return graphql.Null } @@ -9214,7 +12788,7 @@ func (ec *executionContext) _TrivySBOM_serialNumber(ctx context.Context, field g }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SerialNumber, nil + return obj.VulPublishedDate, nil }) if err != nil { ec.Error(ctx, err) @@ -9228,9 +12802,9 @@ func (ec *executionContext) _TrivySBOM_serialNumber(ctx context.Context, field g return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_serialNumber(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9241,8 +12815,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_serialNumber(ctx context.Cont return fc, nil } -func (ec *executionContext) _TrivySBOM_version(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_version(ctx, field) +func (ec *executionContext) _TrivyVul_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_vulLastModifiedDate(ctx, field) if err != nil { return graphql.Null } @@ -9255,7 +12829,7 @@ func (ec *executionContext) _TrivySBOM_version(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Version, nil + return obj.VulLastModifiedDate, nil }) if err != nil { ec.Error(ctx, err) @@ -9264,26 +12838,26 @@ func (ec *executionContext) _TrivySBOM_version(ctx context.Context, field graphq if resTmp == nil { return graphql.Null } - res := resTmp.(*int) + res := resTmp.(*string) fc.Result = res - return ec.marshalOInt2ᚖint(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_version(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TrivySBOM_bomFormat(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_bomFormat(ctx, field) +func (ec *executionContext) _TrivyVul_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_TrivyVul_expiryDate(ctx, field) if err != nil { return graphql.Null } @@ -9296,7 +12870,7 @@ func (ec *executionContext) _TrivySBOM_bomFormat(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.BomFormat, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) @@ -9310,9 +12884,9 @@ func (ec *executionContext) _TrivySBOM_bomFormat(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_bomFormat(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TrivyVul_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "TrivyVul", Field: field, IsMethod: false, IsResolver: false, @@ -9323,8 +12897,8 @@ func (ec *executionContext) fieldContext_TrivySBOM_bomFormat(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivySBOM_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivySbom) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivySBOM_expiryDate(ctx, field) +func (ec *executionContext) _Vulnerability_id(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_id(ctx, field) if err != nil { return graphql.Null } @@ -9337,35 +12911,38 @@ func (ec *executionContext) _TrivySBOM_expiryDate(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivySBOM_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivySBOM", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + return nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TrivyVul_id(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_id(ctx, field) +func (ec *executionContext) _Vulnerability_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_clusterName(ctx, field) if err != nil { return graphql.Null } @@ -9378,7 +12955,7 @@ func (ec *executionContext) _TrivyVul_id(ctx context.Context, field graphql.Coll }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return obj.ClusterName, nil }) if err != nil { ec.Error(ctx, err) @@ -9392,24 +12969,24 @@ func (ec *executionContext) _TrivyVul_id(ctx context.Context, field graphql.Coll } res := resTmp.(string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ID does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _TrivyVul_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_clusterName(ctx, field) +func (ec *executionContext) _Vulnerability_namespace(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_namespace(ctx, field) if err != nil { return graphql.Null } @@ -9422,23 +12999,26 @@ func (ec *executionContext) _TrivyVul_clusterName(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ClusterName, nil + return obj.Namespace, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9449,8 +13029,8 @@ func (ec *executionContext) fieldContext_TrivyVul_clusterName(ctx context.Contex return fc, nil } -func (ec *executionContext) _TrivyVul_namespace(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_namespace(ctx, field) +func (ec *executionContext) _Vulnerability_kind(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_kind(ctx, field) if err != nil { return graphql.Null } @@ -9463,23 +13043,26 @@ func (ec *executionContext) _TrivyVul_namespace(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Namespace, nil + return obj.Kind, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_namespace(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9490,8 +13073,8 @@ func (ec *executionContext) fieldContext_TrivyVul_namespace(ctx context.Context, return fc, nil } -func (ec *executionContext) _TrivyVul_kind(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_kind(ctx, field) +func (ec *executionContext) _Vulnerability_name(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_name(ctx, field) if err != nil { return graphql.Null } @@ -9504,23 +13087,26 @@ func (ec *executionContext) _TrivyVul_kind(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Kind, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_kind(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9531,8 +13117,8 @@ func (ec *executionContext) fieldContext_TrivyVul_kind(ctx context.Context, fiel return fc, nil } -func (ec *executionContext) _TrivyVul_name(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_name(ctx, field) +func (ec *executionContext) _Vulnerability_vulId(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulId(ctx, field) if err != nil { return graphql.Null } @@ -9545,23 +13131,26 @@ func (ec *executionContext) _TrivyVul_name(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.VulID, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*string) + res := resTmp.(string) fc.Result = res - return ec.marshalOString2ᚖstring(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9572,8 +13161,8 @@ func (ec *executionContext) fieldContext_TrivyVul_name(ctx context.Context, fiel return fc, nil } -func (ec *executionContext) _TrivyVul_vulId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulId(ctx, field) +func (ec *executionContext) _Vulnerability_vulVendorIds(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulVendorIds(ctx, field) if err != nil { return graphql.Null } @@ -9586,7 +13175,7 @@ func (ec *executionContext) _TrivyVul_vulId(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulID, nil + return obj.VulVendorIds, nil }) if err != nil { ec.Error(ctx, err) @@ -9600,9 +13189,9 @@ func (ec *executionContext) _TrivyVul_vulId(ctx context.Context, field graphql.C return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulVendorIds(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9613,8 +13202,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulId(ctx context.Context, fie return fc, nil } -func (ec *executionContext) _TrivyVul_vulVendorIds(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulVendorIds(ctx, field) +func (ec *executionContext) _Vulnerability_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulPkgId(ctx, field) if err != nil { return graphql.Null } @@ -9627,7 +13216,7 @@ func (ec *executionContext) _TrivyVul_vulVendorIds(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulVendorIds, nil + return obj.VulPkgID, nil }) if err != nil { ec.Error(ctx, err) @@ -9641,9 +13230,9 @@ func (ec *executionContext) _TrivyVul_vulVendorIds(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulVendorIds(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9654,8 +13243,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulVendorIds(ctx context.Conte return fc, nil } -func (ec *executionContext) _TrivyVul_vulPkgId(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulPkgId(ctx, field) +func (ec *executionContext) _Vulnerability_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulPkgName(ctx, field) if err != nil { return graphql.Null } @@ -9668,7 +13257,7 @@ func (ec *executionContext) _TrivyVul_vulPkgId(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPkgID, nil + return obj.VulPkgName, nil }) if err != nil { ec.Error(ctx, err) @@ -9682,9 +13271,9 @@ func (ec *executionContext) _TrivyVul_vulPkgId(ctx context.Context, field graphq return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulPkgId(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9695,8 +13284,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulPkgId(ctx context.Context, return fc, nil } -func (ec *executionContext) _TrivyVul_vulPkgName(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulPkgName(ctx, field) +func (ec *executionContext) _Vulnerability_vulPkgPath(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulPkgPath(ctx, field) if err != nil { return graphql.Null } @@ -9709,7 +13298,7 @@ func (ec *executionContext) _TrivyVul_vulPkgName(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPkgName, nil + return obj.VulPkgPath, nil }) if err != nil { ec.Error(ctx, err) @@ -9723,9 +13312,9 @@ func (ec *executionContext) _TrivyVul_vulPkgName(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulPkgName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulPkgPath(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9736,8 +13325,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulPkgName(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivyVul_vulPkgPath(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulPkgPath(ctx, field) +func (ec *executionContext) _Vulnerability_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulInstalledVersion(ctx, field) if err != nil { return graphql.Null } @@ -9750,7 +13339,7 @@ func (ec *executionContext) _TrivyVul_vulPkgPath(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPkgPath, nil + return obj.VulInstalledVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -9764,9 +13353,9 @@ func (ec *executionContext) _TrivyVul_vulPkgPath(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulPkgPath(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9777,8 +13366,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulPkgPath(ctx context.Context return fc, nil } -func (ec *executionContext) _TrivyVul_vulInstalledVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulInstalledVersion(ctx, field) +func (ec *executionContext) _Vulnerability_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulFixedVersion(ctx, field) if err != nil { return graphql.Null } @@ -9791,7 +13380,7 @@ func (ec *executionContext) _TrivyVul_vulInstalledVersion(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulInstalledVersion, nil + return obj.VulFixedVersion, nil }) if err != nil { ec.Error(ctx, err) @@ -9805,9 +13394,9 @@ func (ec *executionContext) _TrivyVul_vulInstalledVersion(ctx context.Context, f return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulInstalledVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9818,8 +13407,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulInstalledVersion(ctx contex return fc, nil } -func (ec *executionContext) _TrivyVul_vulFixedVersion(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulFixedVersion(ctx, field) +func (ec *executionContext) _Vulnerability_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulTitle(ctx, field) if err != nil { return graphql.Null } @@ -9832,7 +13421,7 @@ func (ec *executionContext) _TrivyVul_vulFixedVersion(ctx context.Context, field }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulFixedVersion, nil + return obj.VulTitle, nil }) if err != nil { ec.Error(ctx, err) @@ -9846,9 +13435,9 @@ func (ec *executionContext) _TrivyVul_vulFixedVersion(ctx context.Context, field return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulFixedVersion(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9859,8 +13448,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulFixedVersion(ctx context.Co return fc, nil } -func (ec *executionContext) _TrivyVul_vulTitle(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulTitle(ctx, field) +func (ec *executionContext) _Vulnerability_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulSeverity(ctx, field) if err != nil { return graphql.Null } @@ -9873,7 +13462,7 @@ func (ec *executionContext) _TrivyVul_vulTitle(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulTitle, nil + return obj.VulSeverity, nil }) if err != nil { ec.Error(ctx, err) @@ -9887,9 +13476,9 @@ func (ec *executionContext) _TrivyVul_vulTitle(ctx context.Context, field graphq return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulTitle(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9900,8 +13489,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulTitle(ctx context.Context, return fc, nil } -func (ec *executionContext) _TrivyVul_vulSeverity(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulSeverity(ctx, field) +func (ec *executionContext) _Vulnerability_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulPublishedDate(ctx, field) if err != nil { return graphql.Null } @@ -9914,7 +13503,7 @@ func (ec *executionContext) _TrivyVul_vulSeverity(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulSeverity, nil + return obj.VulPublishedDate, nil }) if err != nil { ec.Error(ctx, err) @@ -9928,9 +13517,9 @@ func (ec *executionContext) _TrivyVul_vulSeverity(ctx context.Context, field gra return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulSeverity(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9941,8 +13530,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulSeverity(ctx context.Contex return fc, nil } -func (ec *executionContext) _TrivyVul_vulPublishedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulPublishedDate(ctx, field) +func (ec *executionContext) _Vulnerability_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_vulLastModifiedDate(ctx, field) if err != nil { return graphql.Null } @@ -9955,7 +13544,7 @@ func (ec *executionContext) _TrivyVul_vulPublishedDate(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulPublishedDate, nil + return obj.VulLastModifiedDate, nil }) if err != nil { ec.Error(ctx, err) @@ -9969,9 +13558,9 @@ func (ec *executionContext) _TrivyVul_vulPublishedDate(ctx context.Context, fiel return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulPublishedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -9982,8 +13571,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulPublishedDate(ctx context.C return fc, nil } -func (ec *executionContext) _TrivyVul_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_vulLastModifiedDate(ctx, field) +func (ec *executionContext) _Vulnerability_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_expiryDate(ctx, field) if err != nil { return graphql.Null } @@ -9996,7 +13585,7 @@ func (ec *executionContext) _TrivyVul_vulLastModifiedDate(ctx context.Context, f }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.VulLastModifiedDate, nil + return obj.ExpiryDate, nil }) if err != nil { ec.Error(ctx, err) @@ -10010,9 +13599,9 @@ func (ec *executionContext) _TrivyVul_vulLastModifiedDate(ctx context.Context, f return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_vulLastModifiedDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -10023,8 +13612,8 @@ func (ec *executionContext) fieldContext_TrivyVul_vulLastModifiedDate(ctx contex return fc, nil } -func (ec *executionContext) _TrivyVul_expiryDate(ctx context.Context, field graphql.CollectedField, obj *model.TrivyVul) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_TrivyVul_expiryDate(ctx, field) +func (ec *executionContext) _Vulnerability_exportedAt(ctx context.Context, field graphql.CollectedField, obj *model.Vulnerability) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vulnerability_exportedAt(ctx, field) if err != nil { return graphql.Null } @@ -10037,7 +13626,7 @@ func (ec *executionContext) _TrivyVul_expiryDate(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ExpiryDate, nil + return obj.ExportedAt, nil }) if err != nil { ec.Error(ctx, err) @@ -10051,9 +13640,9 @@ func (ec *executionContext) _TrivyVul_expiryDate(ctx context.Context, field grap return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_TrivyVul_expiryDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Vulnerability_exportedAt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "TrivyVul", + Object: "Vulnerability", Field: field, IsMethod: false, IsResolver: false, @@ -11928,6 +15517,99 @@ func (ec *executionContext) _ClusterAPIsCount(ctx context.Context, sel ast.Selec return out } +var clusterDeletedAPICountImplementors = []string{"ClusterDeletedAPICount"} + +func (ec *executionContext) _ClusterDeletedAPICount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterDeletedAPICount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterDeletedAPICountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterDeletedAPICount") + case "clusterName": + out.Values[i] = ec._ClusterDeletedAPICount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deletedAPICount": + out.Values[i] = ec._ClusterDeletedAPICount_deletedAPICount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var clusterNamespaceMisconfigCountImplementors = []string{"ClusterNamespaceMisconfigCount"} + +func (ec *executionContext) _ClusterNamespaceMisconfigCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceMisconfigCount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterNamespaceMisconfigCountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterNamespaceMisconfigCount") + case "clusterName": + out.Values[i] = ec._ClusterNamespaceMisconfigCount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._ClusterNamespaceMisconfigCount_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "misconfigCount": + out.Values[i] = ec._ClusterNamespaceMisconfigCount_misconfigCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var clusterNamespaceOutdatedCountImplementors = []string{"ClusterNamespaceOutdatedCount"} func (ec *executionContext) _ClusterNamespaceOutdatedCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceOutdatedCount) graphql.Marshaler { @@ -12026,6 +15708,55 @@ func (ec *executionContext) _ClusterNamespaceResourceCount(ctx context.Context, return out } +var clusterNamespaceVulCountImplementors = []string{"ClusterNamespaceVulCount"} + +func (ec *executionContext) _ClusterNamespaceVulCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceVulCount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterNamespaceVulCountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterNamespaceVulCount") + case "clusterName": + out.Values[i] = ec._ClusterNamespaceVulCount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._ClusterNamespaceVulCount_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "vulCount": + out.Values[i] = ec._ClusterNamespaceVulCount_vulCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var deletedAPIImplementors = []string{"DeletedAPI"} func (ec *executionContext) _DeletedAPI(ctx context.Context, sel ast.SelectionSet, obj *model.DeletedAPI) graphql.Marshaler { @@ -12387,9 +16118,95 @@ func (ec *executionContext) _Kubescore(ctx context.Context, sel ast.SelectionSet case "fileRow": out.Values[i] = ec._Kubescore_fileRow(ctx, field, obj) case "eventTime": - out.Values[i] = ec._Kubescore_eventTime(ctx, field, obj) + out.Values[i] = ec._Kubescore_eventTime(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var misconfigurationImplementors = []string{"Misconfiguration"} + +func (ec *executionContext) _Misconfiguration(ctx context.Context, sel ast.SelectionSet, obj *model.Misconfiguration) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, misconfigurationImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Misconfiguration") + case "id": + out.Values[i] = ec._Misconfiguration_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._Misconfiguration_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._Misconfiguration_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._Misconfiguration_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._Misconfiguration_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "misconfigId": + out.Values[i] = ec._Misconfiguration_misconfigId(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "misconfigAvdid": + out.Values[i] = ec._Misconfiguration_misconfigAvdid(ctx, field, obj) + case "misconfigType": + out.Values[i] = ec._Misconfiguration_misconfigType(ctx, field, obj) + case "misconfigTitle": + out.Values[i] = ec._Misconfiguration_misconfigTitle(ctx, field, obj) + case "misconfigDesc": + out.Values[i] = ec._Misconfiguration_misconfigDesc(ctx, field, obj) + case "misconfigMsg": + out.Values[i] = ec._Misconfiguration_misconfigMsg(ctx, field, obj) + case "misconfigQuery": + out.Values[i] = ec._Misconfiguration_misconfigQuery(ctx, field, obj) + case "misconfigResolution": + out.Values[i] = ec._Misconfiguration_misconfigResolution(ctx, field, obj) + case "misconfigSeverity": + out.Values[i] = ec._Misconfiguration_misconfigSeverity(ctx, field, obj) + case "misconfigStatus": + out.Values[i] = ec._Misconfiguration_misconfigStatus(ctx, field, obj) + case "eventTime": + out.Values[i] = ec._Misconfiguration_eventTime(ctx, field, obj) case "expiryDate": - out.Values[i] = ec._Kubescore_expiryDate(ctx, field, obj) + out.Values[i] = ec._Misconfiguration_expiryDate(ctx, field, obj) + case "exportedAt": + out.Values[i] = ec._Misconfiguration_exportedAt(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -12863,7 +16680,271 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "outdatedImagesByClusterAndNamespace": + case "outdatedImagesByClusterAndNamespace": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_outdatedImagesByClusterAndNamespace(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "outdatedImagesCount": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_outdatedImagesCount(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterNamespaceOutdatedCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterNamespaceOutdatedCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterDeprecatedAPIsCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterDeprecatedAPIsCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterDeletedAPIsCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterDeletedAPIsCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "allClusterNamespaceResourceCounts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_allClusterNamespaceResourceCounts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "eventsByClusterAndNamespace": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_eventsByClusterAndNamespace(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "vulnerabilities": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_vulnerabilities(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "misconfigurations": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_misconfigurations(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "kubescores": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_kubescores(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "getAllResources": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_getAllResources(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "trivyImages": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_trivyImages(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "deprecatedAPIs": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12872,7 +16953,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_outdatedImagesByClusterAndNamespace(ctx, field) + res = ec._Query_deprecatedAPIs(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12885,7 +16966,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "outdatedImagesCount": + case "deletedAPIs": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12894,7 +16975,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_outdatedImagesCount(ctx, field) + res = ec._Query_deletedAPIs(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12907,7 +16988,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "allClusterNamespaceOutdatedCounts": + case "trivySBOMs": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12916,7 +16997,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_allClusterNamespaceOutdatedCounts(ctx, field) + res = ec._Query_trivySBOMs(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12929,7 +17010,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "allClusterDeprecatedAPIsCounts": + case "trivyVulCount": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12938,7 +17019,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_allClusterDeprecatedAPIsCounts(ctx, field) + res = ec._Query_trivyVulCount(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12951,7 +17032,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "allClusterDeletedAPIsCounts": + case "trivyMisconfigCount": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12960,7 +17041,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_allClusterDeletedAPIsCounts(ctx, field) + res = ec._Query_trivyMisconfigCount(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12973,7 +17054,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "allClusterNamespaceResourceCounts": + case "deletedAPICount": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -12982,7 +17063,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_allClusterNamespaceResourceCounts(ctx, field) + res = ec._Query_deletedAPICount(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -12995,7 +17076,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) - case "eventsByClusterAndNamespace": + case "trivyImageCount": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -13004,7 +17085,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_eventsByClusterAndNamespace(ctx, field) + res = ec._Query_trivyImageCount(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -13225,6 +17306,50 @@ func (ec *executionContext) _TrivyImage(ctx context.Context, sel ast.SelectionSe return out } +var trivyImageCountImplementors = []string{"TrivyImageCount"} + +func (ec *executionContext) _TrivyImageCount(ctx context.Context, sel ast.SelectionSet, obj *model.TrivyImageCount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, trivyImageCountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("TrivyImageCount") + case "clusterName": + out.Values[i] = ec._TrivyImageCount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "ImageCount": + out.Values[i] = ec._TrivyImageCount_ImageCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var trivyMisconfigImplementors = []string{"TrivyMisconfig"} func (ec *executionContext) _TrivyMisconfig(ctx context.Context, sel ast.SelectionSet, obj *model.TrivyMisconfig) graphql.Marshaler { @@ -13424,6 +17549,94 @@ func (ec *executionContext) _TrivyVul(ctx context.Context, sel ast.SelectionSet, return out } +var vulnerabilityImplementors = []string{"Vulnerability"} + +func (ec *executionContext) _Vulnerability(ctx context.Context, sel ast.SelectionSet, obj *model.Vulnerability) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, vulnerabilityImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Vulnerability") + case "id": + out.Values[i] = ec._Vulnerability_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "clusterName": + out.Values[i] = ec._Vulnerability_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "namespace": + out.Values[i] = ec._Vulnerability_namespace(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "kind": + out.Values[i] = ec._Vulnerability_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._Vulnerability_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "vulId": + out.Values[i] = ec._Vulnerability_vulId(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "vulVendorIds": + out.Values[i] = ec._Vulnerability_vulVendorIds(ctx, field, obj) + case "vulPkgId": + out.Values[i] = ec._Vulnerability_vulPkgId(ctx, field, obj) + case "vulPkgName": + out.Values[i] = ec._Vulnerability_vulPkgName(ctx, field, obj) + case "vulPkgPath": + out.Values[i] = ec._Vulnerability_vulPkgPath(ctx, field, obj) + case "vulInstalledVersion": + out.Values[i] = ec._Vulnerability_vulInstalledVersion(ctx, field, obj) + case "vulFixedVersion": + out.Values[i] = ec._Vulnerability_vulFixedVersion(ctx, field, obj) + case "vulTitle": + out.Values[i] = ec._Vulnerability_vulTitle(ctx, field, obj) + case "vulSeverity": + out.Values[i] = ec._Vulnerability_vulSeverity(ctx, field, obj) + case "vulPublishedDate": + out.Values[i] = ec._Vulnerability_vulPublishedDate(ctx, field, obj) + case "vulLastModifiedDate": + out.Values[i] = ec._Vulnerability_vulLastModifiedDate(ctx, field, obj) + case "expiryDate": + out.Values[i] = ec._Vulnerability_expiryDate(ctx, field, obj) + case "exportedAt": + out.Values[i] = ec._Vulnerability_exportedAt(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var __DirectiveImplementors = []string{"__Directive"} func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { @@ -13873,6 +18086,34 @@ func (ec *executionContext) marshalNClusterAPIsCount2ᚖgithubᚗcomᚋintelops return ec._ClusterAPIsCount(ctx, sel, v) } +func (ec *executionContext) marshalNClusterDeletedAPICount2githubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterDeletedAPICount(ctx context.Context, sel ast.SelectionSet, v model.ClusterDeletedAPICount) graphql.Marshaler { + return ec._ClusterDeletedAPICount(ctx, sel, &v) +} + +func (ec *executionContext) marshalNClusterDeletedAPICount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterDeletedAPICount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterDeletedAPICount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterDeletedAPICount(ctx, sel, v) +} + +func (ec *executionContext) marshalNClusterNamespaceMisconfigCount2githubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceMisconfigCount(ctx context.Context, sel ast.SelectionSet, v model.ClusterNamespaceMisconfigCount) graphql.Marshaler { + return ec._ClusterNamespaceMisconfigCount(ctx, sel, &v) +} + +func (ec *executionContext) marshalNClusterNamespaceMisconfigCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceMisconfigCount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterNamespaceMisconfigCount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterNamespaceMisconfigCount(ctx, sel, v) +} + func (ec *executionContext) marshalNClusterNamespaceOutdatedCount2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceOutdatedCountᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.ClusterNamespaceOutdatedCount) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -13981,6 +18222,20 @@ func (ec *executionContext) marshalNClusterNamespaceResourceCount2ᚖgithubᚗco return ec._ClusterNamespaceResourceCount(ctx, sel, v) } +func (ec *executionContext) marshalNClusterNamespaceVulCount2githubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceVulCount(ctx context.Context, sel ast.SelectionSet, v model.ClusterNamespaceVulCount) graphql.Marshaler { + return ec._ClusterNamespaceVulCount(ctx, sel, &v) +} + +func (ec *executionContext) marshalNClusterNamespaceVulCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceVulCount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterNamespaceVulCount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterNamespaceVulCount(ctx, sel, v) +} + func (ec *executionContext) marshalNDeletedAPI2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐDeletedAPIᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.DeletedAPI) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -14335,6 +18590,60 @@ func (ec *executionContext) marshalNKubescore2ᚖgithubᚗcomᚋintelopsᚋkubvi return ec._Kubescore(ctx, sel, v) } +func (ec *executionContext) marshalNMisconfiguration2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐMisconfigurationᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Misconfiguration) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNMisconfiguration2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐMisconfiguration(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNMisconfiguration2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐMisconfiguration(ctx context.Context, sel ast.SelectionSet, v *model.Misconfiguration) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Misconfiguration(ctx, sel, v) +} + func (ec *executionContext) marshalNNamespace2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐNamespaceᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Namespace) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -14620,6 +18929,20 @@ func (ec *executionContext) marshalNTrivyImage2ᚖgithubᚗcomᚋintelopsᚋkubv return ec._TrivyImage(ctx, sel, v) } +func (ec *executionContext) marshalNTrivyImageCount2githubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageCount(ctx context.Context, sel ast.SelectionSet, v model.TrivyImageCount) graphql.Marshaler { + return ec._TrivyImageCount(ctx, sel, &v) +} + +func (ec *executionContext) marshalNTrivyImageCount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyImageCount(ctx context.Context, sel ast.SelectionSet, v *model.TrivyImageCount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._TrivyImageCount(ctx, sel, v) +} + func (ec *executionContext) marshalNTrivyMisconfig2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐTrivyMisconfigᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.TrivyMisconfig) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -14782,6 +19105,60 @@ func (ec *executionContext) marshalNTrivyVul2ᚖgithubᚗcomᚋintelopsᚋkubviz return ec._TrivyVul(ctx, sel, v) } +func (ec *executionContext) marshalNVulnerability2ᚕᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐVulnerabilityᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Vulnerability) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNVulnerability2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐVulnerability(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNVulnerability2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐVulnerability(ctx context.Context, sel ast.SelectionSet, v *model.Vulnerability) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Vulnerability(ctx, sel, v) +} + func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { return ec.___Directive(ctx, sel, &v) } diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index 0048aa56..d31563ce 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -11,6 +11,17 @@ type ClusterAPIsCount struct { Count int `json:"count"` } +type ClusterDeletedAPICount struct { + ClusterName string `json:"clusterName"` + DeletedAPICount int `json:"deletedAPICount"` +} + +type ClusterNamespaceMisconfigCount struct { + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + MisconfigCount int `json:"misconfigCount"` +} + type ClusterNamespaceOutdatedCount struct { ClusterName string `json:"clusterName"` Namespace string `json:"namespace"` @@ -23,6 +34,12 @@ type ClusterNamespaceResourceCount struct { ResourceCount int `json:"resourceCount"` } +type ClusterNamespaceVulCount struct { + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + VulCount int `json:"vulCount"` +} + type DeletedAPI struct { ClusterName *string `json:"ClusterName,omitempty"` ObjectName *string `json:"ObjectName,omitempty"` @@ -107,7 +124,27 @@ type Kubescore struct { FileName *string `json:"fileName,omitempty"` FileRow *int `json:"fileRow,omitempty"` EventTime *string `json:"eventTime,omitempty"` - ExpiryDate *string `json:"expiryDate,omitempty"` +} + +type Misconfiguration struct { + ID string `json:"id"` + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + Kind string `json:"kind"` + Name string `json:"name"` + MisconfigID string `json:"misconfigId"` + MisconfigAvdid *string `json:"misconfigAvdid,omitempty"` + MisconfigType *string `json:"misconfigType,omitempty"` + MisconfigTitle *string `json:"misconfigTitle,omitempty"` + MisconfigDesc *string `json:"misconfigDesc,omitempty"` + MisconfigMsg *string `json:"misconfigMsg,omitempty"` + MisconfigQuery *string `json:"misconfigQuery,omitempty"` + MisconfigResolution *string `json:"misconfigResolution,omitempty"` + MisconfigSeverity *string `json:"misconfigSeverity,omitempty"` + MisconfigStatus *string `json:"misconfigStatus,omitempty"` + EventTime *string `json:"eventTime,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` + ExportedAt *string `json:"exportedAt,omitempty"` } type Namespace struct { @@ -171,6 +208,11 @@ type TrivyImage struct { ExpiryDate *string `json:"expiryDate,omitempty"` } +type TrivyImageCount struct { + ClusterName string `json:"clusterName"` + ImageCount int `json:"ImageCount"` +} + type TrivyMisconfig struct { ID string `json:"id"` ClusterName *string `json:"clusterName,omitempty"` @@ -223,3 +265,24 @@ type TrivyVul struct { VulLastModifiedDate *string `json:"vulLastModifiedDate,omitempty"` ExpiryDate *string `json:"expiryDate,omitempty"` } + +type Vulnerability struct { + ID string `json:"id"` + ClusterName string `json:"clusterName"` + Namespace string `json:"namespace"` + Kind string `json:"kind"` + Name string `json:"name"` + VulID string `json:"vulId"` + VulVendorIds *string `json:"vulVendorIds,omitempty"` + VulPkgID *string `json:"vulPkgId,omitempty"` + VulPkgName *string `json:"vulPkgName,omitempty"` + VulPkgPath *string `json:"vulPkgPath,omitempty"` + VulInstalledVersion *string `json:"vulInstalledVersion,omitempty"` + VulFixedVersion *string `json:"vulFixedVersion,omitempty"` + VulTitle *string `json:"vulTitle,omitempty"` + VulSeverity *string `json:"vulSeverity,omitempty"` + VulPublishedDate *string `json:"vulPublishedDate,omitempty"` + VulLastModifiedDate *string `json:"vulLastModifiedDate,omitempty"` + ExpiryDate *string `json:"expiryDate,omitempty"` + ExportedAt *string `json:"exportedAt,omitempty"` +} diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index cd5978a6..07e588fa 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -18,7 +18,105 @@ type Query { allClusterDeletedAPIsCounts: [ClusterAPIsCount!]! allClusterNamespaceResourceCounts: [ClusterNamespaceResourceCount!]! eventsByClusterAndNamespace(clusterName: String!, namespace: String!): [Event!]! + vulnerabilities(clusterName: String!, namespace: String!): [Vulnerability!]! + misconfigurations(clusterName: String!, namespace: String!): [Misconfiguration!]! + kubescores(clustername: String!, namespace: String!): [KubeScore!]! + getAllResources(clusterName: String!, namespace: String!): [GetAllResource!]! + trivyImages(clusterName: String!): [TrivyImage!]! + deprecatedAPIs(clusterName: String!): [DeprecatedAPI!]! + deletedAPIs(clusterName: String!): [DeletedAPI!]! + trivySBOMs(clusterName: String!): [TrivySBOM!]! + trivyVulCount(clusterName: String!, namespace: String!): ClusterNamespaceVulCount! + trivyMisconfigCount(clusterName: String!, namespace: String!): ClusterNamespaceMisconfigCount! + deletedAPICount(clusterName: String!): ClusterDeletedAPICount! + trivyImageCount(clusterName: String!): TrivyImageCount! } + +type TrivyImageCount { + clusterName: String! + ImageCount: Int! +} + + +type ClusterDeletedAPICount { + clusterName: String! + deletedAPICount: Int! +} + + +type ClusterNamespaceMisconfigCount { + clusterName: String! + namespace: String! + misconfigCount: Int! +} + + +type ClusterNamespaceVulCount { + clusterName: String! + namespace: String! + vulCount: Int! +} + +type Kubescore { + id: ID! + clusterName: String + objectName: String + kind: String + apiVersion: String + name: String + namespace: String + targetType: String + description: String + path: String + summary: String + fileName: String + fileRow: Int + eventTime: String +} + + +type Misconfiguration { + id: ID! + clusterName: String! + namespace: String! + kind: String! + name: String! + misconfigId: String! + misconfigAvdid: String + misconfigType: String + misconfigTitle: String + misconfigDesc: String + misconfigMsg: String + misconfigQuery: String + misconfigResolution: String + misconfigSeverity: String + misconfigStatus: String + eventTime: String + expiryDate: String + exportedAt: String +} + +type Vulnerability { + id: ID! + clusterName: String! + namespace: String! + kind: String! + name: String! + vulId: String! + vulVendorIds: String + vulPkgId: String + vulPkgName: String + vulPkgPath: String + vulInstalledVersion: String + vulFixedVersion: String + vulTitle: String + vulSeverity: String + vulPublishedDate: String + vulLastModifiedDate: String + expiryDate: String + exportedAt: String +} + type Event { ClusterName: String Id: String @@ -102,24 +200,6 @@ type TrivyVul { expiryDate: String } -type Kubescore { - id: ID! - clusterName: String - objectName: String - kind: String - apiVersion: String - name: String - namespace: String - targetType: String - description: String - path: String - summary: String - fileName: String - fileRow: Int - eventTime: String - expiryDate: String -} - type TrivyImage { id: ID! clusterName: String diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index 40c6e177..9ded4e4c 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -275,7 +275,7 @@ func (r *queryResolver) AllKubeScores(ctx context.Context) ([]*model.Kubescore, var kubeScores []*model.Kubescore for rows.Next() { var ks model.Kubescore - if err := rows.Scan(&ks.ID, &ks.ClusterName, &ks.ObjectName, &ks.Kind, &ks.APIVersion, &ks.Name, &ks.Namespace, &ks.TargetType, &ks.Description, &ks.Path, &ks.Summary, &ks.FileName, &ks.FileRow, &ks.EventTime, &ks.ExpiryDate); err != nil { + if err := rows.Scan(&ks.ID, &ks.ClusterName, &ks.ObjectName, &ks.Kind, &ks.APIVersion, &ks.Name, &ks.Namespace, &ks.TargetType, &ks.Description, &ks.Path, &ks.Summary, &ks.FileName, &ks.FileRow, &ks.EventTime); err != nil { return nil, fmt.Errorf("error scanning row: %v", err) } kubeScores = append(kubeScores, &ks) @@ -616,6 +616,400 @@ func (r *queryResolver) EventsByClusterAndNamespace(ctx context.Context, cluster return events, nil } +// Vulnerabilities is the resolver for the vulnerabilities field. +func (r *queryResolver) Vulnerabilities(ctx context.Context, clusterName string, namespace string) ([]*model.Vulnerability, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + query := ` + SELECT id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate, ExportedAt + FROM trivy_vul + WHERE cluster_name = ? AND namespace = ? + ` + rows, err := r.DB.QueryContext(ctx, query, clusterName, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + var vulnerabilities []*model.Vulnerability + for rows.Next() { + var v model.Vulnerability + if err := rows.Scan(&v.ID, &v.ClusterName, &v.Namespace, &v.Kind, &v.Name, &v.VulID, &v.VulVendorIds, &v.VulPkgID, &v.VulPkgName, &v.VulPkgPath, &v.VulInstalledVersion, &v.VulFixedVersion, &v.VulTitle, &v.VulSeverity, &v.VulPublishedDate, &v.VulLastModifiedDate, &v.ExpiryDate, &v.ExportedAt); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + vulnerabilities = append(vulnerabilities, &v) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return vulnerabilities, nil +} + +// Misconfigurations is the resolver for the misconfigurations field. +func (r *queryResolver) Misconfigurations(ctx context.Context, clusterName string, namespace string) ([]*model.Misconfiguration, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := ` + SELECT id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime, ExpiryDate, ExportedAt + FROM trivy_misconfig + WHERE cluster_name = ? AND namespace = ? + ` + + rows, err := r.DB.QueryContext(ctx, query, clusterName, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var misconfigurations []*model.Misconfiguration + for rows.Next() { + var m model.Misconfiguration + if err := rows.Scan(&m.ID, &m.ClusterName, &m.Namespace, &m.Kind, &m.Name, &m.MisconfigID, &m.MisconfigAvdid, &m.MisconfigType, &m.MisconfigTitle, &m.MisconfigDesc, &m.MisconfigMsg, &m.MisconfigQuery, &m.MisconfigResolution, &m.MisconfigSeverity, &m.MisconfigStatus, &m.EventTime, &m.ExpiryDate, &m.ExportedAt); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + misconfigurations = append(misconfigurations, &m) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return misconfigurations, nil +} + +// Kubescores is the resolver for the kubescores field. +func (r *queryResolver) Kubescores(ctx context.Context, clustername string, namespace string) ([]*model.KubeScore, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clustername == "" || namespace == "" { + return nil, fmt.Errorf("clustername and namespace cannot be empty") + } + + query := ` + SELECT id, clustername, object_name, kind, apiVersion, name, namespace, target_type, description, path, summary, file_name, file_row, EventTime, ExpiryDate, ExportedAt + FROM kubescore + WHERE clustername = ? AND namespace = ? + ` + + rows, err := r.DB.QueryContext(ctx, query, clustername, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var scores []*model.KubeScore + for rows.Next() { + var s model.KubeScore + if err := rows.Scan(&s.ID, &s.ClusterName, &s.ObjectName, &s.Kind, &s.APIVersion, &s.Name, &s.Namespace, &s.TargetType, &s.Description, &s.Path, &s.Summary, &s.FileName, &s.FileRow, &s.EventTime); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + scores = append(scores, &s) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return scores, nil +} + +// GetAllResources is the resolver for the getAllResources field. +func (r *queryResolver) GetAllResources(ctx context.Context, clusterName string, namespace string) ([]*model.GetAllResource, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := ` + SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime, ExpiryDate, ExportedAt + FROM getall_resources + WHERE ClusterName = ? AND Namespace = ? + ` + + rows, err := r.DB.QueryContext(ctx, query, clusterName, namespace) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var resources []*model.GetAllResource + for rows.Next() { + var r model.GetAllResource + if err := rows.Scan(&r.ClusterName, &r.Namespace, &r.Kind, &r.Resource, &r.Age, &r.EventTime, &r.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + resources = append(resources, &r) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return resources, nil +} + +// TrivyImages is the resolver for the trivyImages field. +func (r *queryResolver) TrivyImages(ctx context.Context, clusterName string) ([]*model.TrivyImage, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("clusterName cannot be empty") + } + + query := ` + SELECT id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate, ExportedAt + FROM trivyimage + WHERE cluster_name = ? + ` + + rows, err := r.DB.QueryContext(ctx, query, clusterName) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var images []*model.TrivyImage + for rows.Next() { + var img model.TrivyImage + if err := rows.Scan(&img.ID, &img.ClusterName, &img.ArtifactName, &img.VulID, &img.VulPkgID, &img.VulPkgName, &img.VulInstalledVersion, &img.VulFixedVersion, &img.VulTitle, &img.VulSeverity, &img.VulPublishedDate, &img.VulLastModifiedDate, &img.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + images = append(images, &img) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return images, nil +} + +// DeprecatedAPIs is the resolver for the deprecatedAPIs field. +func (r *queryResolver) DeprecatedAPIs(ctx context.Context, clusterName string) ([]*model.DeprecatedAPI, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("ClusterName cannot be empty") + } + + query := ` + SELECT ClusterName, ObjectName, Description, Kind, Deprecated, Scope, EventTime, ExpiryDate, ExportedAt + FROM DeprecatedAPIs + WHERE ClusterName = ? + ` + + rows, err := r.DB.QueryContext(ctx, query, clusterName) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + var apis []*model.DeprecatedAPI + for rows.Next() { + var api model.DeprecatedAPI + var deprecated uint8 + if err := rows.Scan(&api.ClusterName, &api.ObjectName, &api.Description, &api.Kind, &deprecated, &api.Scope, &api.EventTime, &api.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + deprecatedBool := deprecated != 0 + api.Deprecated = &deprecatedBool + apis = append(apis, &api) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return apis, nil +} + +// DeletedAPIs is the resolver for the deletedAPIs field. +func (r *queryResolver) DeletedAPIs(ctx context.Context, clusterName string) ([]*model.DeletedAPI, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("ClusterName cannot be empty") + } + + query := ` + SELECT ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope, EventTime, ExpiryDate, ExportedAt + FROM DeletedAPIs + WHERE ClusterName = ? + ` + rows, err := r.DB.QueryContext(ctx, query, clusterName) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + var apis []*model.DeletedAPI + for rows.Next() { + var api model.DeletedAPI + var deleted uint8 + if err := rows.Scan(&api.ClusterName, &api.ObjectName, &api.Group, &api.Kind, &api.Version, &api.Name, &deleted, &api.Scope, &api.EventTime, &api.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + deletedBool := deleted != 0 + api.Deleted = &deletedBool + apis = append(apis, &api) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return apis, nil +} + +// TrivySBOMs is the resolver for the trivySBOMs field. +func (r *queryResolver) TrivySBOMs(ctx context.Context, clusterName string) ([]*model.TrivySbom, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("clusterName cannot be empty") + } + + query := ` + SELECT id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format, ExpiryDate, ExportedAt + FROM trivysbom + WHERE cluster_name = ? + ` + + rows, err := r.DB.QueryContext(ctx, query, clusterName) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + defer rows.Close() + + var sboms []*model.TrivySbom + for rows.Next() { + var sbom model.TrivySbom + if err := rows.Scan(&sbom.ID, &sbom.ClusterName, &sbom.ImageName, &sbom.PackageName, &sbom.PackageURL, &sbom.BomRef, &sbom.SerialNumber, &sbom.Version, &sbom.BomFormat, &sbom.ExpiryDate); err != nil { + return nil, fmt.Errorf("error scanning row: %v", err) + } + sboms = append(sboms, &sbom) + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error iterating rows: %v", err) + } + + return sboms, nil +} + +// TrivyVulCount is the resolver for the trivyVulCount field. +func (r *queryResolver) TrivyVulCount(ctx context.Context, clusterName string, namespace string) (*model.ClusterNamespaceVulCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := `SELECT COUNT(*) FROM trivy_vul WHERE cluster_name = ? AND namespace = ?` + + var count int + err := r.DB.QueryRowContext(ctx, query, clusterName, namespace).Scan(&count) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + + return &model.ClusterNamespaceVulCount{ + ClusterName: clusterName, + Namespace: namespace, + VulCount: count, + }, nil +} + +// TrivyMisconfigCount is the resolver for the trivyMisconfigCount field. +func (r *queryResolver) TrivyMisconfigCount(ctx context.Context, clusterName string, namespace string) (*model.ClusterNamespaceMisconfigCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" || namespace == "" { + return nil, fmt.Errorf("clusterName and namespace cannot be empty") + } + + query := `SELECT COUNT(*) FROM trivy_misconfig WHERE cluster_name = ? AND namespace = ?` + + var count int + err := r.DB.QueryRowContext(ctx, query, clusterName, namespace).Scan(&count) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + + return &model.ClusterNamespaceMisconfigCount{ + ClusterName: clusterName, + Namespace: namespace, + MisconfigCount: count, + }, nil +} + +// DeletedAPICount is the resolver for the deletedAPICount field. +func (r *queryResolver) DeletedAPICount(ctx context.Context, clusterName string) (*model.ClusterDeletedAPICount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("ClusterName cannot be empty") + } + + query := `SELECT COUNT(*) FROM DeletedAPIs WHERE ClusterName = ?` + + var count int + err := r.DB.QueryRowContext(ctx, query, clusterName).Scan(&count) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + + return &model.ClusterDeletedAPICount{ + ClusterName: clusterName, + DeletedAPICount: count, + }, nil +} + +// TrivyImageVulCount is the resolver for the trivyImageVulCount field. +func (r *queryResolver) TrivyImageCount(ctx context.Context, clusterName string) (*model.TrivyImageCount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("clusterName cannot be empty") + } + + query := `SELECT COUNT(*) FROM trivyimage WHERE cluster_name = ?` + + var count int + err := r.DB.QueryRowContext(ctx, query, clusterName).Scan(&count) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + + return &model.TrivyImageCount{ + ClusterName: clusterName, + ImageCount: count, + }, nil +} + // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } diff --git a/graphqlserver/server.go b/graphqlserver/server.go index f16730f5..8e191aff 100644 --- a/graphqlserver/server.go +++ b/graphqlserver/server.go @@ -23,7 +23,7 @@ const ( func main() { log.Println("Graph ql server starting ... Iteration one") - cfg := &config.Config{} + cfg := &config.GraphQlConfig{} if err := envconfig.Process("", cfg); err != nil { log.Fatalf("Could not parse env Config: %v", err) } @@ -46,12 +46,17 @@ func main() { log.Fatal(http.ListenAndServe(":"+port, nil)) } -func initializeDatabase(cfg *config.Config) (*sql.DB, error) { +func initializeDatabase(cfg *config.GraphQlConfig) (*sql.DB, error) { var db *sql.DB var err error - + var config = &config.Config{ + DbPort: cfg.DbPort, + DBAddress: cfg.DBAddress, + ClickHouseUsername: cfg.ClickHouseUsername, + ClickHousePassword: cfg.ClickHousePassword, + } for i := 0; i < maxRetries; i++ { - _, db, err = clickhouse.NewDBClient(cfg) + _, db, err = clickhouse.NewDBClient(config) if err == nil { log.Println("Successfully connected to the database") return db, nil From a9b5ad0023d1609b00fe2ccd51365ebfa8ff711e Mon Sep 17 00:00:00 2001 From: vijeyash Date: Fri, 1 Mar 2024 16:15:41 +0530 Subject: [PATCH 232/263] graphqpis bugs resolved --- graphqlserver/graph/generated.go | 277 ++++++++++++++++++++++++ graphqlserver/graph/model/models_gen.go | 5 + graphqlserver/graph/schema.graphqls | 6 + graphqlserver/graph/schema.resolvers.go | 40 +++- 4 files changed, 320 insertions(+), 8 deletions(-) diff --git a/graphqlserver/graph/generated.go b/graphqlserver/graph/generated.go index 8b58dd37..7a1d76b0 100644 --- a/graphqlserver/graph/generated.go +++ b/graphqlserver/graph/generated.go @@ -60,6 +60,11 @@ type ComplexityRoot struct { DeletedAPICount func(childComplexity int) int } + ClusterDeprecatedAPICount struct { + ClusterName func(childComplexity int) int + DeprecatedAPICount func(childComplexity int) int + } + ClusterNamespaceMisconfigCount struct { ClusterName func(childComplexity int) int MisconfigCount func(childComplexity int) int @@ -230,6 +235,7 @@ type ComplexityRoot struct { AllTrivyVuls func(childComplexity int) int DeletedAPICount func(childComplexity int, clusterName string) int DeletedAPIs func(childComplexity int, clusterName string) int + DeprecatedAPICount func(childComplexity int, clusterName string) int DeprecatedAPIs func(childComplexity int, clusterName string) int EventsByClusterAndNamespace func(childComplexity int, clusterName string, namespace string) int GetAllResources func(childComplexity int, clusterName string, namespace string) int @@ -395,6 +401,7 @@ type QueryResolver interface { TrivyMisconfigCount(ctx context.Context, clusterName string, namespace string) (*model.ClusterNamespaceMisconfigCount, error) DeletedAPICount(ctx context.Context, clusterName string) (*model.ClusterDeletedAPICount, error) TrivyImageCount(ctx context.Context, clusterName string) (*model.TrivyImageCount, error) + DeprecatedAPICount(ctx context.Context, clusterName string) (*model.ClusterDeprecatedAPICount, error) } type executableSchema struct { @@ -451,6 +458,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ClusterDeletedAPICount.DeletedAPICount(childComplexity), true + case "ClusterDeprecatedAPICount.clusterName": + if e.complexity.ClusterDeprecatedAPICount.ClusterName == nil { + break + } + + return e.complexity.ClusterDeprecatedAPICount.ClusterName(childComplexity), true + + case "ClusterDeprecatedAPICount.deprecatedAPICount": + if e.complexity.ClusterDeprecatedAPICount.DeprecatedAPICount == nil { + break + } + + return e.complexity.ClusterDeprecatedAPICount.DeprecatedAPICount(childComplexity), true + case "ClusterNamespaceMisconfigCount.clusterName": if e.complexity.ClusterNamespaceMisconfigCount.ClusterName == nil { break @@ -1350,6 +1371,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.DeletedAPIs(childComplexity, args["clusterName"].(string)), true + case "Query.deprecatedAPICount": + if e.complexity.Query.DeprecatedAPICount == nil { + break + } + + args, err := ec.field_Query_deprecatedAPICount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.DeprecatedAPICount(childComplexity, args["clusterName"].(string)), true + case "Query.deprecatedAPIs": if e.complexity.Query.DeprecatedAPIs == nil { break @@ -2315,6 +2348,21 @@ func (ec *executionContext) field_Query_deletedAPIs_args(ctx context.Context, ra return args, nil } +func (ec *executionContext) field_Query_deprecatedAPICount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["clusterName"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("clusterName")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["clusterName"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_deprecatedAPIs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -2864,6 +2912,94 @@ func (ec *executionContext) fieldContext_ClusterDeletedAPICount_deletedAPICount( return fc, nil } +func (ec *executionContext) _ClusterDeprecatedAPICount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterDeprecatedAPICount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterDeprecatedAPICount_clusterName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ClusterName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterDeprecatedAPICount_clusterName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterDeprecatedAPICount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ClusterDeprecatedAPICount_deprecatedAPICount(ctx context.Context, field graphql.CollectedField, obj *model.ClusterDeprecatedAPICount) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ClusterDeprecatedAPICount_deprecatedAPICount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecatedAPICount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ClusterDeprecatedAPICount_deprecatedAPICount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ClusterDeprecatedAPICount", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ClusterNamespaceMisconfigCount_clusterName(ctx context.Context, field graphql.CollectedField, obj *model.ClusterNamespaceMisconfigCount) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ClusterNamespaceMisconfigCount_clusterName(ctx, field) if err != nil { @@ -9739,6 +9875,67 @@ func (ec *executionContext) fieldContext_Query_trivyImageCount(ctx context.Conte return fc, nil } +func (ec *executionContext) _Query_deprecatedAPICount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_deprecatedAPICount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().DeprecatedAPICount(rctx, fc.Args["clusterName"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.ClusterDeprecatedAPICount) + fc.Result = res + return ec.marshalNClusterDeprecatedAPICount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterDeprecatedAPICount(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_deprecatedAPICount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "clusterName": + return ec.fieldContext_ClusterDeprecatedAPICount_clusterName(ctx, field) + case "deprecatedAPICount": + return ec.fieldContext_ClusterDeprecatedAPICount_deprecatedAPICount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ClusterDeprecatedAPICount", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_deprecatedAPICount_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -15561,6 +15758,50 @@ func (ec *executionContext) _ClusterDeletedAPICount(ctx context.Context, sel ast return out } +var clusterDeprecatedAPICountImplementors = []string{"ClusterDeprecatedAPICount"} + +func (ec *executionContext) _ClusterDeprecatedAPICount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterDeprecatedAPICount) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, clusterDeprecatedAPICountImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ClusterDeprecatedAPICount") + case "clusterName": + out.Values[i] = ec._ClusterDeprecatedAPICount_clusterName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecatedAPICount": + out.Values[i] = ec._ClusterDeprecatedAPICount_deprecatedAPICount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var clusterNamespaceMisconfigCountImplementors = []string{"ClusterNamespaceMisconfigCount"} func (ec *executionContext) _ClusterNamespaceMisconfigCount(ctx context.Context, sel ast.SelectionSet, obj *model.ClusterNamespaceMisconfigCount) graphql.Marshaler { @@ -17097,6 +17338,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "deprecatedAPICount": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_deprecatedAPICount(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -18100,6 +18363,20 @@ func (ec *executionContext) marshalNClusterDeletedAPICount2ᚖgithubᚗcomᚋint return ec._ClusterDeletedAPICount(ctx, sel, v) } +func (ec *executionContext) marshalNClusterDeprecatedAPICount2githubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterDeprecatedAPICount(ctx context.Context, sel ast.SelectionSet, v model.ClusterDeprecatedAPICount) graphql.Marshaler { + return ec._ClusterDeprecatedAPICount(ctx, sel, &v) +} + +func (ec *executionContext) marshalNClusterDeprecatedAPICount2ᚖgithubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterDeprecatedAPICount(ctx context.Context, sel ast.SelectionSet, v *model.ClusterDeprecatedAPICount) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ClusterDeprecatedAPICount(ctx, sel, v) +} + func (ec *executionContext) marshalNClusterNamespaceMisconfigCount2githubᚗcomᚋintelopsᚋkubvizᚋgraphqlserverᚋgraphᚋmodelᚐClusterNamespaceMisconfigCount(ctx context.Context, sel ast.SelectionSet, v model.ClusterNamespaceMisconfigCount) graphql.Marshaler { return ec._ClusterNamespaceMisconfigCount(ctx, sel, &v) } diff --git a/graphqlserver/graph/model/models_gen.go b/graphqlserver/graph/model/models_gen.go index d31563ce..41e4076b 100644 --- a/graphqlserver/graph/model/models_gen.go +++ b/graphqlserver/graph/model/models_gen.go @@ -16,6 +16,11 @@ type ClusterDeletedAPICount struct { DeletedAPICount int `json:"deletedAPICount"` } +type ClusterDeprecatedAPICount struct { + ClusterName string `json:"clusterName"` + DeprecatedAPICount int `json:"deprecatedAPICount"` +} + type ClusterNamespaceMisconfigCount struct { ClusterName string `json:"clusterName"` Namespace string `json:"namespace"` diff --git a/graphqlserver/graph/schema.graphqls b/graphqlserver/graph/schema.graphqls index 07e588fa..9b6e3258 100644 --- a/graphqlserver/graph/schema.graphqls +++ b/graphqlserver/graph/schema.graphqls @@ -30,6 +30,12 @@ type Query { trivyMisconfigCount(clusterName: String!, namespace: String!): ClusterNamespaceMisconfigCount! deletedAPICount(clusterName: String!): ClusterDeletedAPICount! trivyImageCount(clusterName: String!): TrivyImageCount! + deprecatedAPICount(clusterName: String!): ClusterDeprecatedAPICount! +} + +type ClusterDeprecatedAPICount { + clusterName: String! + deprecatedAPICount: Int! } type TrivyImageCount { diff --git a/graphqlserver/graph/schema.resolvers.go b/graphqlserver/graph/schema.resolvers.go index 9ded4e4c..71cb5330 100644 --- a/graphqlserver/graph/schema.resolvers.go +++ b/graphqlserver/graph/schema.resolvers.go @@ -626,7 +626,7 @@ func (r *queryResolver) Vulnerabilities(ctx context.Context, clusterName string, return nil, fmt.Errorf("clusterName and namespace cannot be empty") } query := ` - SELECT id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate, ExportedAt + SELECT id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate FROM trivy_vul WHERE cluster_name = ? AND namespace = ? ` @@ -661,7 +661,7 @@ func (r *queryResolver) Misconfigurations(ctx context.Context, clusterName strin } query := ` - SELECT id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime, ExpiryDate, ExportedAt + SELECT id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime, ExpiryDate FROM trivy_misconfig WHERE cluster_name = ? AND namespace = ? ` @@ -698,7 +698,7 @@ func (r *queryResolver) Kubescores(ctx context.Context, clustername string, name } query := ` - SELECT id, clustername, object_name, kind, apiVersion, name, namespace, target_type, description, path, summary, file_name, file_row, EventTime, ExpiryDate, ExportedAt + SELECT id, clustername, object_name, kind, apiVersion, name, namespace, target_type, description, path, summary, file_name, file_row, EventTime, ExpiryDate FROM kubescore WHERE clustername = ? AND namespace = ? ` @@ -735,7 +735,7 @@ func (r *queryResolver) GetAllResources(ctx context.Context, clusterName string, } query := ` - SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime, ExpiryDate, ExportedAt + SELECT ClusterName, Namespace, Kind, Resource, Age, EventTime, ExpiryDate FROM getall_resources WHERE ClusterName = ? AND Namespace = ? ` @@ -772,7 +772,7 @@ func (r *queryResolver) TrivyImages(ctx context.Context, clusterName string) ([] } query := ` - SELECT id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate, ExportedAt + SELECT id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date, ExpiryDate FROM trivyimage WHERE cluster_name = ? ` @@ -809,7 +809,7 @@ func (r *queryResolver) DeprecatedAPIs(ctx context.Context, clusterName string) } query := ` - SELECT ClusterName, ObjectName, Description, Kind, Deprecated, Scope, EventTime, ExpiryDate, ExportedAt + SELECT ClusterName, ObjectName, Description, Kind, Deprecated, Scope, EventTime, ExpiryDate FROM DeprecatedAPIs WHERE ClusterName = ? ` @@ -848,7 +848,7 @@ func (r *queryResolver) DeletedAPIs(ctx context.Context, clusterName string) ([] } query := ` - SELECT ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope, EventTime, ExpiryDate, ExportedAt + SELECT ClusterName, ObjectName, Group, Kind, Version, Name, Deleted, Scope, EventTime, ExpiryDate FROM DeletedAPIs WHERE ClusterName = ? ` @@ -886,7 +886,7 @@ func (r *queryResolver) TrivySBOMs(ctx context.Context, clusterName string) ([]* } query := ` - SELECT id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format, ExpiryDate, ExportedAt + SELECT id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format, ExpiryDate FROM trivysbom WHERE cluster_name = ? ` @@ -1010,6 +1010,30 @@ func (r *queryResolver) TrivyImageCount(ctx context.Context, clusterName string) }, nil } +// DeprecatedAPICount is the resolver for the deprecatedAPICount field. +func (r *queryResolver) DeprecatedAPICount(ctx context.Context, clusterName string) (*model.ClusterDeprecatedAPICount, error) { + if r.DB == nil { + return nil, fmt.Errorf("database connection is not initialized") + } + + if clusterName == "" { + return nil, fmt.Errorf("ClusterName cannot be empty") + } + + query := `SELECT COUNT(*) FROM DeprecatedAPIs WHERE ClusterName = ?` + + var count int + err := r.DB.QueryRowContext(ctx, query, clusterName).Scan(&count) + if err != nil { + return nil, fmt.Errorf("error executing query: %v", err) + } + + return &model.ClusterDeprecatedAPICount{ + ClusterName: clusterName, + DeprecatedAPICount: count, + }, nil +} + // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } From c396f0d1c8cc2d5869a7ad39fadee6f9708f8c43 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 12 Mar 2024 11:50:04 +0530 Subject: [PATCH 233/263] changed kuberhealthy frequency --- agent/config/config.go | 2 +- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agent/config/config.go b/agent/config/config.go index 632b3332..020db830 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -30,7 +30,7 @@ func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { type KHConfig struct { KuberhealthyURL string `envconfig:"KUBERHEALTHY_URL" required:"true"` - PollInterval time.Duration `envconfig:"POLL_INTERVAL" default:"15m"` + PollInterval time.Duration `envconfig:"POLL_INTERVAL" default:"60m"` } func GetKuberHealthyConfig() (khconfig *KHConfig, err error) { diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 694dc262..6b3b7924 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.14 +version: 1.1.15 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index f119e92a..a2c5841d 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -188,7 +188,7 @@ schedule: kuberhealthy: enabled: true - pollInterval: "15m" + pollInterval: "60m" url: "http://localhost:8080" opentelemetry: From 030cf44d581f440087c655cd28c7716b2b637edc Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Fri, 15 Mar 2024 10:23:47 +0530 Subject: [PATCH 234/263] New trivy dashboard JSON --- charts/client/Chart.yaml | 2 +- .../templates/configmap-trivy-dashboard.yaml | 2446 ++++++++++++----- grafana/trivy-dashboard.json | 2446 ++++++++++++----- 3 files changed, 3401 insertions(+), 1493 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index ebc9d780..4baa724f 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.19 +version: 1.1.20 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 8589def1..d762bc19 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -32,141 +32,39 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 170, + "id": 106, "links": [], "liveNow": false, "panels": [ { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, "gridPos": { - "h": 8, - "w": 12, + "h": 1, + "w": 24, "x": 0, "y": 0 }, - "id": 36, - "options": { - "baidu": { - "callback": "bmapReady", - "key": "" - }, - "editor": { - "format": "auto", - "height": 600 - }, - "gaode": { - "key": "", - "plugin": "AMap.Scale,AMap.ToolBar" - }, - "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'vul_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", - "google": { - "callback": "gmapReady", - "key": "" - }, - "map": "none", - "renderer": "canvas", - "themeEditor": { - "config": "{}", - "height": 400, - "name": "default" - } - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_severity", - "rawQuery": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694438766) AND vul_last_modified_date <= toDateTime(1694611566)\nGROUP BY vul_severity", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Vulnerability Severity Distribution", - "type": "volkovlabs-echarts-panel" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 37, - "options": { - "baidu": { - "callback": "bmapReady", - "key": "" - }, - "editor": { - "format": "auto", - "height": 600 - }, - "gaode": { - "key": "", - "plugin": "AMap.Scale,AMap.ToolBar" - }, - "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'misconfig_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", - "google": { - "callback": "gmapReady", - "key": "" - }, - "map": "none", - "renderer": "canvas", - "themeEditor": { - "config": "{}", - "height": 400, - "name": "default" - } - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY misconfig_severity", - "rawQuery": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694438912) AND EventTime <= toDateTime(1694611712)\nGROUP BY misconfig_severity", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Misconfiguration Severity Distribution", - "type": "volkovlabs-echarts-panel" + "id": 49, + "title": "Image Vulnerability and SBOM", + "type": "row" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel illustrates the distribution of vulnerability severities across different clusters. It provides an overview of the count of vulnerabilities categorized by severity levels within each cluster.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -174,34 +72,65 @@ data: "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, + { + "color": "orange", + "value": 25 + }, { "color": "red", - "value": 80 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 8 + "y": 1 }, - "id": 20, + "id": 47, "options": { - "displayMode": "gradient", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -215,29 +144,34 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, vul_severity", - "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155614) AND vul_last_modified_date <= toDateTime(1694242014)\nGROUP BY cluster_name, vul_severity", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Severity counts grouped by Cluster", - "type": "bargauge" + "title": "Highest Vulnerability Images with Low Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel illustrates the distribution of misconfiguration severities across different clusters. It provides an overview of the count of misconfigurations categorized by severity levels within each cluster. ", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -245,34 +179,65 @@ data: "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, + { + "color": "orange", + "value": 25 + }, { "color": "red", - "value": 80 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 8 + "y": 1 }, - "id": 22, + "id": 48, "options": { - "displayMode": "gradient", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -286,62 +251,100 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, misconfig_severity", - "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156239) AND EventTime <= toDateTime(1694242639)\nGROUP BY cluster_name, misconfig_severity", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Severity counts grouped by Cluster", - "type": "bargauge" + "title": "Highest Vulnerability Images with High Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total number Vulnerabilities under each namespace", "fieldConfig": { "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, { "color": "orange", - "value": 70 + "value": 25 }, { "color": "red", - "value": 85 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { - "h": 5, + "h": 8, "w": 12, "x": 0, - "y": 16 + "y": 9 }, - "id": 18, + "id": 45, "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -355,62 +358,100 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155665) AND vul_last_modified_date <= toDateTime(1694242065)\nGROUP BY cluster_name", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Count by Cluster", - "type": "gauge" + "title": "Highest Vulnerability Images with Medium Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel displays the total of misconfigurations from each clusters.", "fieldConfig": { "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, { "color": "orange", - "value": 70 + "value": 25 }, { "color": "red", - "value": 85 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { - "h": 5, + "h": 8, "w": 12, "x": 12, - "y": 16 + "y": 9 }, - "id": 16, + "id": 46, "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -424,64 +465,81 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156266) AND EventTime <= toDateTime(1694242666)\nGROUP BY cluster_name", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Count by Cluster", - "type": "gauge" + "title": "Highest Vulnerability Images with Critical Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of high vulnerabilities categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "light-blue", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 0, - "y": 21 + "y": 17 }, - "id": 29, + "id": 43, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -495,64 +553,81 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155706) AND vul_last_modified_date <= toDateTime(1694242106) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "High Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Low Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of high misconfigurations categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 12, - "y": 21 + "y": 17 }, - "id": 30, + "id": 44, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -566,64 +641,81 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156303) AND EventTime <= toDateTime(1694242703) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "High Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Medium Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of Low vulnerabilities categorized by namespace. It helps to monitor and prioritize Low security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "super-light-orange", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 0, - "y": 28 + "y": 25 }, - "id": 27, + "id": 41, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -637,64 +729,81 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155745) AND vul_last_modified_date <= toDateTime(1694242145) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Low Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "High Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of low misconfigurations categorized by namespace. It helps to monitor and prioritize low security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ - { - "color": "green", - "value": null - }, { "color": "red", - "value": 80 + "value": null } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 12, - "y": 28 + "y": 25 }, - "id": 28, + "id": 42, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -708,29 +817,35 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156331) AND EventTime <= toDateTime(1694242731) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Low Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Critical Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of Medium vulnerabilities categorized by namespace. It helps to monitor and prioritize Medium security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -748,24 +863,23 @@ data: "overrides": [] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 0, - "y": 35 + "y": 33 }, - "id": 25, + "id": 39, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -779,29 +893,32 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155776) AND vul_last_modified_date <= toDateTime(1694242176) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "query": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", + "rawQuery": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Medium Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "duplicate package for sbom images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of medium misconfigurations categorized by namespace. It helps to monitor and prioritize medium security issues across different namespaces.", "fieldConfig": { "defaults": { - "color": { - "mode": "continuous-GrYlRd" + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -816,27 +933,42 @@ data: ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "images" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 12, - "y": 35 + "y": 33 }, - "id": 26, + "id": 40, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -850,39 +982,42 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156357) AND EventTime <= toDateTime(1694242757) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", + "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Medium Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Count of images across Vulnerability Id", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", + "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -890,24 +1025,23 @@ data: "overrides": [] }, "gridPos": { - "h": 7, - "w": 12, + "h": 8, + "w": 24, "x": 0, - "y": 42 + "y": 41 }, - "id": 12, + "id": 34, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -921,39 +1055,42 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156016) AND vul_last_modified_date <= toDateTime(1694242416) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Critical Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Trivy Image", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "description": "This panel provides a count of critical misconfigurations categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", + "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -961,24 +1098,23 @@ data: "overrides": [] }, "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 42 + "h": 8, + "w": 24, + "x": 0, + "y": 49 }, - "id": 14, + "id": 35, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -991,451 +1127,1269 @@ data: "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "interval": "", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156383) AND EventTime <= toDateTime(1694242783) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT * FROM default.trivysbom", + "rawQuery": "SELECT * FROM default.trivysbom", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Critical Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Trivy_SBOM", + "type": "table" }, { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, + "collapsed": true, "gridPos": { - "h": 8, - "w": 12, + "h": 1, + "w": 24, "x": 0, - "y": 49 - }, - "id": 6, - "options": { - "displayMode": "lcd", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], - "fields": "", - "values": true - }, - "showUnfilled": true, - "valueMode": "color" + "y": 57 }, - "pluginVersion": "10.0.3", - "targets": [ + "id": 38, + "panels": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156175) AND vul_last_modified_date <= toDateTime(1694242575)\nGROUP BY cluster_name, namespace", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Vulnerability Count by Cluster and Namespace", - "type": "bargauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 97 }, - "mappings": [], - "noValue": "Trivy Misconfigurations not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "id": 36, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'vul_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" }, - { - "color": "red", - "value": 80 - } - ] - } + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_severity", + "rawQuery": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694438766) AND vul_last_modified_date <= toDateTime(1694611566)\nGROUP BY vul_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Severity Distribution", + "type": "volkovlabs-echarts-panel" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 49 - }, - "id": 4, - "options": { - "displayMode": "lcd", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], - "fields": "", - "values": true + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 97 + }, + "id": 37, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'misconfig_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY misconfig_severity", + "rawQuery": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694438912) AND EventTime <= toDateTime(1694611712)\nGROUP BY misconfig_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Severity Distribution", + "type": "volkovlabs-echarts-panel" }, - "showUnfilled": true, - "valueMode": "color" - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156545) AND EventTime <= toDateTime(1694242945)\nGROUP BY cluster_name, namespace", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Misconfiguration Count by Cluster and Namespace", - "type": "bargauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel illustrates the distribution of vulnerability severities across different clusters. It provides an overview of the count of vulnerabilities categorized by severity levels within each cluster.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 105 + }, + "id": 20, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showUnfilled": true, + "valueMode": "color" }, - "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, vul_severity", + "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155614) AND vul_last_modified_date <= toDateTime(1694242014)\nGROUP BY cluster_name, vul_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Severity counts grouped by Cluster", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel illustrates the distribution of misconfiguration severities across different clusters. It provides an overview of the count of misconfigurations categorized by severity levels within each cluster. ", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 105 + }, + "id": 22, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, misconfig_severity", + "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156239) AND EventTime <= toDateTime(1694242639)\nGROUP BY cluster_name, misconfig_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Severity counts grouped by Cluster", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 57 - }, - "id": 32, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel displays the total number Vulnerabilities under each namespace", + "fieldConfig": { + "defaults": { + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green" + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 113 + }, + "id": 18, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155665) AND vul_last_modified_date <= toDateTime(1694242065)\nGROUP BY cluster_name", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "Vulnerability Count by Cluster", + "type": "gauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", - "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE vul_last_modified_date >= toDateTime(1694099993) AND vul_last_modified_date <= toDateTime(1694186393)\nORDER BY vul_last_modified_date DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Trivy Vulnerabilities", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel displays the total of misconfigurations from each clusters.", + "fieldConfig": { + "defaults": { + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green" + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 113 + }, + "id": 16, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showThresholdLabels": false, + "showThresholdMarkers": true }, - "mappings": [], - "noValue": "Trivy Misconfigurations not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156266) AND EventTime <= toDateTime(1694242666)\nGROUP BY cluster_name", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Count by Cluster", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of high vulnerabilities categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 118 + }, + "id": 29, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155706) AND vul_last_modified_date <= toDateTime(1694242106) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "High Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 65 - }, - "id": 33, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of high misconfigurations categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 118 + }, + "id": 30, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156303) AND EventTime <= toDateTime(1694242703) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "High Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", - "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Trivy Misconfiguration", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel provides a count of Low vulnerabilities categorized by namespace. It helps to monitor and prioritize Low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 125 + }, + "id": 27, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showUnfilled": true, + "valueMode": "color" }, - "mappings": [], - "noValue": "Trivy Image not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155745) AND vul_last_modified_date <= toDateTime(1694242145) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Low Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of low misconfigurations categorized by namespace. It helps to monitor and prioritize low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 125 + }, + "id": 28, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156331) AND EventTime <= toDateTime(1694242731) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Low Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 73 - }, - "id": 34, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of Medium vulnerabilities categorized by namespace. It helps to monitor and prioritize Medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 132 + }, + "id": 25, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155776) AND vul_last_modified_date <= toDateTime(1694242176) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "Medium Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", - "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Trivy Image", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "{{ .Values.datasources.uid }}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel provides a count of medium misconfigurations categorized by namespace. It helps to monitor and prioritize medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 132 + }, + "id": 26, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showUnfilled": true, + "valueMode": "color" }, - "mappings": [], - "noValue": "Trivy SBOM not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156357) AND EventTime <= toDateTime(1694242757) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Medium Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 139 + }, + "id": 12, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156016) AND vul_last_modified_date <= toDateTime(1694242416) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Critical Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 81 - }, - "id": 35, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel provides a count of critical misconfigurations categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 139 + }, + "id": 14, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "interval": "", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156383) AND EventTime <= toDateTime(1694242783) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "Critical Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.trivysbom", - "rawQuery": "SELECT * FROM default.trivysbom", - "refId": "A", - "round": "0s", - "skip_comments": true + "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 146 + }, + "id": 6, + "options": { + "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156175) AND vul_last_modified_date <= toDateTime(1694242575)\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Count by Cluster and Namespace", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 146 + }, + "id": 4, + "options": { + "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156545) AND EventTime <= toDateTime(1694242945)\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Count by Cluster and Namespace", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 154 + }, + "id": 32, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE vul_last_modified_date >= toDateTime(1694099993) AND vul_last_modified_date <= toDateTime(1694186393)\nORDER BY vul_last_modified_date DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Trivy Vulnerabilities", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 162 + }, + "id": 33, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Trivy Misconfiguration", + "type": "table" } ], - "title": "Trivy_SBOM", - "type": "table" + "title": "Trivy Vulnerability and Misconfiguration", + "type": "row" } ], "refresh": "", @@ -1453,7 +2407,7 @@ data: "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 1, + "version": 13, "weekStart": "" } {{- end }} \ No newline at end of file diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 3bb3deef..db35cd83 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,141 +21,39 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 170, + "id": 106, "links": [], "liveNow": false, "panels": [ { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, "gridPos": { - "h": 8, - "w": 12, + "h": 1, + "w": 24, "x": 0, "y": 0 }, - "id": 36, - "options": { - "baidu": { - "callback": "bmapReady", - "key": "" - }, - "editor": { - "format": "auto", - "height": 600 - }, - "gaode": { - "key": "", - "plugin": "AMap.Scale,AMap.ToolBar" - }, - "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'vul_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", - "google": { - "callback": "gmapReady", - "key": "" - }, - "map": "none", - "renderer": "canvas", - "themeEditor": { - "config": "{}", - "height": 400, - "name": "default" - } - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_severity", - "rawQuery": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694438766) AND vul_last_modified_date <= toDateTime(1694611566)\nGROUP BY vul_severity", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Vulnerability Severity Distribution", - "type": "volkovlabs-echarts-panel" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 37, - "options": { - "baidu": { - "callback": "bmapReady", - "key": "" - }, - "editor": { - "format": "auto", - "height": 600 - }, - "gaode": { - "key": "", - "plugin": "AMap.Scale,AMap.ToolBar" - }, - "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'misconfig_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", - "google": { - "callback": "gmapReady", - "key": "" - }, - "map": "none", - "renderer": "canvas", - "themeEditor": { - "config": "{}", - "height": 400, - "name": "default" - } - }, - "targets": [ - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY misconfig_severity", - "rawQuery": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694438912) AND EventTime <= toDateTime(1694611712)\nGROUP BY misconfig_severity", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Misconfiguration Severity Distribution", - "type": "volkovlabs-echarts-panel" + "id": 49, + "title": "Image Vulnerability and SBOM", + "type": "row" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel illustrates the distribution of vulnerability severities across different clusters. It provides an overview of the count of vulnerabilities categorized by severity levels within each cluster.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -163,34 +61,65 @@ "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, + { + "color": "orange", + "value": 25 + }, { "color": "red", - "value": 80 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 8 + "y": 1 }, - "id": 20, + "id": 47, "options": { - "displayMode": "gradient", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -204,29 +133,34 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, vul_severity", - "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155614) AND vul_last_modified_date <= toDateTime(1694242014)\nGROUP BY cluster_name, vul_severity", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Severity counts grouped by Cluster", - "type": "bargauge" + "title": "Highest Vulnerability Images with Low Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel illustrates the distribution of misconfiguration severities across different clusters. It provides an overview of the count of misconfigurations categorized by severity levels within each cluster. ", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -234,34 +168,65 @@ "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, + { + "color": "orange", + "value": 25 + }, { "color": "red", - "value": 80 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 8 + "y": 1 }, - "id": 22, + "id": 48, "options": { - "displayMode": "gradient", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -275,62 +240,100 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, misconfig_severity", - "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156239) AND EventTime <= toDateTime(1694242639)\nGROUP BY cluster_name, misconfig_severity", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Severity counts grouped by Cluster", - "type": "bargauge" + "title": "Highest Vulnerability Images with High Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total number Vulnerabilities under each namespace", "fieldConfig": { "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, { "color": "orange", - "value": 70 + "value": 25 }, { "color": "red", - "value": 85 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { - "h": 5, + "h": 8, "w": 12, "x": 0, - "y": 16 + "y": 9 }, - "id": 18, + "id": 45, "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -344,62 +347,100 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155665) AND vul_last_modified_date <= toDateTime(1694242065)\nGROUP BY cluster_name", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Vulnerability Count by Cluster", - "type": "gauge" + "title": "Highest Vulnerability Images with Medium Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel displays the total of misconfigurations from each clusters.", "fieldConfig": { "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { - "mode": "percentage", + "mode": "absolute", "steps": [ { "color": "green", "value": null }, + { + "color": "semi-dark-yellow", + "value": 10 + }, { "color": "orange", - "value": 70 + "value": 25 }, { "color": "red", - "value": 85 + "value": 50 + }, + { + "color": "semi-dark-red", + "value": 100 + }, + { + "color": "dark-red", + "value": 1000 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Counts" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { - "h": 5, + "h": 8, "w": 12, "x": 12, - "y": 16 + "y": 9 }, - "id": 16, + "id": 46, "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showThresholdLabels": false, - "showThresholdMarkers": true + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -413,64 +454,81 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name", - "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156266) AND EventTime <= toDateTime(1694242666)\nGROUP BY cluster_name", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Misconfiguration Count by Cluster", - "type": "gauge" + "title": "Highest Vulnerability Images with Critical Severity", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of high vulnerabilities categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "light-blue", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 0, - "y": 21 + "y": 17 }, - "id": 29, + "id": 43, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -484,64 +542,81 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155706) AND vul_last_modified_date <= toDateTime(1694242106) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "High Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Low Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of high misconfigurations categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 12, - "y": 21 + "y": 17 }, - "id": 30, + "id": 44, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -555,64 +630,81 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156303) AND EventTime <= toDateTime(1694242703) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "High Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Medium Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of Low vulnerabilities categorized by namespace. It helps to monitor and prioritize Low security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ { - "color": "green", + "color": "super-light-orange", "value": null - }, - { - "color": "red", - "value": 80 } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 0, - "y": 28 + "y": 25 }, - "id": 27, + "id": 41, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -626,64 +718,81 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155745) AND vul_last_modified_date <= toDateTime(1694242145) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Low Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "High Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of low misconfigurations categorized by namespace. It helps to monitor and prioritize low security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ - { - "color": "green", - "value": null - }, { "color": "red", - "value": 80 + "value": null } ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "vul_severity" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "color-background" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 12, - "y": 28 + "y": 25 }, - "id": 28, + "id": 42, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -697,29 +806,35 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156331) AND EventTime <= toDateTime(1694242731) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Low Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Critical Vulnerability Images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of Medium vulnerabilities categorized by namespace. It helps to monitor and prioritize Medium security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", "thresholds": { "mode": "absolute", "steps": [ @@ -737,24 +852,23 @@ "overrides": [] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 0, - "y": 35 + "y": 33 }, - "id": 25, + "id": 39, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -768,29 +882,32 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155776) AND vul_last_modified_date <= toDateTime(1694242176) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "query": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", + "rawQuery": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Medium Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "duplicate package for sbom images", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of medium misconfigurations categorized by namespace. It helps to monitor and prioritize medium security issues across different namespaces.", "fieldConfig": { "defaults": { - "color": { - "mode": "continuous-GrYlRd" + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", "thresholds": { "mode": "absolute", "steps": [ @@ -805,27 +922,42 @@ ] } }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "images" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "mode": "lcd", + "type": "gauge" + } + } + ] + } + ] }, "gridPos": { - "h": 7, + "h": 8, "w": 12, "x": 12, - "y": 35 + "y": 33 }, - "id": 26, + "id": 40, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -839,39 +971,42 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156357) AND EventTime <= toDateTime(1694242757) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", + "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Medium Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Count of images across Vulnerability Id", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", + "noValue": "Trivy Image not available", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -879,24 +1014,23 @@ "overrides": [] }, "gridPos": { - "h": 7, - "w": 12, + "h": 8, + "w": 24, "x": 0, - "y": 42 + "y": 41 }, - "id": 12, + "id": 34, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -910,39 +1044,42 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156016) AND vul_last_modified_date <= toDateTime(1694242416) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Critical Vulnerability Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Trivy Image", + "type": "table" }, { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "description": "This panel provides a count of critical misconfigurations categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", "fieldConfig": { "defaults": { "color": { - "mode": "continuous-GrYlRd" + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false }, "mappings": [], - "noValue": "Trivy Misconfigurations not available", + "noValue": "Trivy SBOM not available", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -950,24 +1087,23 @@ "overrides": [] }, "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 42 + "h": 8, + "w": 24, + "x": 0, + "y": 49 }, - "id": 14, + "id": 35, "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], + "cellHeight": "sm", + "footer": { + "countRows": false, "fields": "", - "values": true + "reducer": [ + "sum" + ], + "show": false }, - "showUnfilled": true, - "valueMode": "color" + "showHeader": true }, "pluginVersion": "10.0.3", "targets": [ @@ -980,451 +1116,1269 @@ "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "interval": "", "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", - "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156383) AND EventTime <= toDateTime(1694242783) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "query": "SELECT * FROM default.trivysbom", + "rawQuery": "SELECT * FROM default.trivysbom", "refId": "A", "round": "0s", "skip_comments": true } ], - "title": "Critical Misconfiguration Count by Namespace and ClusterName", - "type": "bargauge" + "title": "Trivy_SBOM", + "type": "table" }, { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, + "collapsed": true, "gridPos": { - "h": 8, - "w": 12, + "h": 1, + "w": 24, "x": 0, - "y": 49 - }, - "id": 6, - "options": { - "displayMode": "lcd", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], - "fields": "", - "values": true - }, - "showUnfilled": true, - "valueMode": "color" + "y": 57 }, - "pluginVersion": "10.0.3", - "targets": [ + "id": 38, + "panels": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156175) AND vul_last_modified_date <= toDateTime(1694242575)\nGROUP BY cluster_name, namespace", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Vulnerability Count by Cluster and Namespace", - "type": "bargauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 97 }, - "mappings": [], - "noValue": "Trivy Misconfigurations not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "id": 36, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'vul_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" }, - { - "color": "red", - "value": 80 - } - ] - } + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_severity", + "rawQuery": "SELECT vul_severity, count(*) AS total_count\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694438766) AND vul_last_modified_date <= toDateTime(1694611566)\nGROUP BY vul_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Severity Distribution", + "type": "volkovlabs-echarts-panel" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 49 - }, - "id": 4, - "options": { - "displayMode": "lcd", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], - "fields": "", - "values": true + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 97 + }, + "id": 37, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let severity = [];\nlet counts = [];\n\ndata.series.map((s) => {\n severity = s.fields.find((f) => f.name === 'misconfig_severity').values;\n counts = s.fields.find((f) => f.name === 'total_count').values;\n});\n\n// Create an empty array to store pie chart data\nconst pieChartData = [];\n\n// Define colors for pie slices\nconst pieSliceColors = ['#235894', '#FF0000', '#00FF00', '#FFFF00', '#FFA500'];\n\n// Map severity and counts to pie chart data\nseverity.forEach((sev, index) => {\n pieChartData.push({\n value: counts[index],\n name: sev,\n itemStyle: {\n opacity: 0.7,\n color: pieSliceColors[index % pieSliceColors.length],\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {},\n series: [\n {\n name: 'pie',\n type: 'pie',\n selectedMode: 'single',\n selectedOffset: 30,\n clockwise: true,\n label: {\n fontSize: 18,\n color: '#235894',\n },\n labelLine: {\n lineStyle: {\n color: '#235894',\n },\n },\n data: pieChartData, // Use the modified pie chart data\n itemStyle: {\n opacity: 0.7,\n borderWidth: 3,\n borderColor: '#FFFFFF', // Set the border color to white\n },\n },\n ],\n};", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + } + }, + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY misconfig_severity", + "rawQuery": "SELECT misconfig_severity, count(*) AS total_count\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694438912) AND EventTime <= toDateTime(1694611712)\nGROUP BY misconfig_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Severity Distribution", + "type": "volkovlabs-echarts-panel" }, - "showUnfilled": true, - "valueMode": "color" - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, namespace", - "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156545) AND EventTime <= toDateTime(1694242945)\nGROUP BY cluster_name, namespace", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Misconfiguration Count by Cluster and Namespace", - "type": "bargauge" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel illustrates the distribution of vulnerability severities across different clusters. It provides an overview of the count of vulnerabilities categorized by severity levels within each cluster.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 105 + }, + "id": 20, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showUnfilled": true, + "valueMode": "color" }, - "mappings": [], - "noValue": "Trivy Vulnerabilities Not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, vul_severity", + "rawQuery": "SELECT cluster_name, vul_severity, count(*) \nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155614) AND vul_last_modified_date <= toDateTime(1694242014)\nGROUP BY cluster_name, vul_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Severity counts grouped by Cluster", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel illustrates the distribution of misconfiguration severities across different clusters. It provides an overview of the count of misconfigurations categorized by severity levels within each cluster. ", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 105 + }, + "id": 22, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, misconfig_severity", + "rawQuery": "SELECT cluster_name, misconfig_severity, count(*)\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156239) AND EventTime <= toDateTime(1694242639)\nGROUP BY cluster_name, misconfig_severity", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Severity counts grouped by Cluster", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 57 - }, - "id": 32, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the total number Vulnerabilities under each namespace", + "fieldConfig": { + "defaults": { + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green" + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 113 + }, + "id": 18, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155665) AND vul_last_modified_date <= toDateTime(1694242065)\nGROUP BY cluster_name", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "Vulnerability Count by Cluster", + "type": "gauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", - "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE vul_last_modified_date >= toDateTime(1694099993) AND vul_last_modified_date <= toDateTime(1694186393)\nORDER BY vul_last_modified_date DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Trivy Vulnerabilities", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel displays the total of misconfigurations from each clusters.", + "fieldConfig": { + "defaults": { + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green" + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 113 + }, + "id": 16, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showThresholdLabels": false, + "showThresholdMarkers": true }, - "mappings": [], - "noValue": "Trivy Misconfigurations not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name", + "rawQuery": "SELECT cluster_name, count(*) FROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156266) AND EventTime <= toDateTime(1694242666)\nGROUP BY cluster_name", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Count by Cluster", + "type": "gauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of high vulnerabilities categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 118 + }, + "id": 29, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS High_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155706) AND vul_last_modified_date <= toDateTime(1694242106) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "High Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 65 - }, - "id": 33, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of high misconfigurations categorized by namespace. It helps to monitor and prioritize high security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 118 + }, + "id": 30, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS High_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156303) AND EventTime <= toDateTime(1694242703) AND misconfig_severity = 'HIGH'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "High Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", - "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Trivy Misconfiguration", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel provides a count of Low vulnerabilities categorized by namespace. It helps to monitor and prioritize Low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 125 + }, + "id": 27, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showUnfilled": true, + "valueMode": "color" }, - "mappings": [], - "noValue": "Trivy Image not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Low_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155745) AND vul_last_modified_date <= toDateTime(1694242145) AND vul_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Low Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of low misconfigurations categorized by namespace. It helps to monitor and prioritize low security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 125 + }, + "id": 28, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Low_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156331) AND EventTime <= toDateTime(1694242731) AND misconfig_severity = 'LOW'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Low Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 73 - }, - "id": 34, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of Medium vulnerabilities categorized by namespace. It helps to monitor and prioritize Medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 132 + }, + "id": 25, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Medium_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694155776) AND vul_last_modified_date <= toDateTime(1694242176) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "Medium Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", - "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", - "refId": "A", - "round": "0s", - "skip_comments": true - } - ], - "title": "Trivy Image", - "type": "table" - }, - { - "datasource": { - "type": "vertamedia-clickhouse-datasource", - "uid": "vertamedia-clickhouse-datasource" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "description": "This panel provides a count of medium misconfigurations categorized by namespace. It helps to monitor and prioritize medium security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] }, - "custom": { - "align": "center", - "cellOptions": { - "type": "color-text" + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 132 + }, + "id": 26, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true }, - "filterable": true, - "inspect": false + "showUnfilled": true, + "valueMode": "color" }, - "mappings": [], - "noValue": "Trivy SBOM not available", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Medium_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156357) AND EventTime <= toDateTime(1694242757) AND misconfig_severity = 'MEDIUM'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Medium Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of critical vulnerabilities categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ] - } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 139 + }, + "id": 12, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(vul_severity) AS Critical_Severity\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156016) AND vul_last_modified_date <= toDateTime(1694242416) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Critical Vulnerability Count by Namespace and ClusterName", + "type": "bargauge" }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 81 - }, - "id": 35, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel provides a count of critical misconfigurations categorized by namespace. It helps to monitor and prioritize critical security issues across different namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 139 + }, + "id": 14, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "interval": "", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace\n", + "rawQuery": "SELECT cluster_name, namespace, count(misconfig_severity) AS Critical_Severity\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156383) AND EventTime <= toDateTime(1694242783) AND misconfig_severity = 'CRITICAL'\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } ], - "show": false + "title": "Critical Misconfiguration Count by Namespace and ClusterName", + "type": "bargauge" }, - "showHeader": true - }, - "pluginVersion": "10.0.3", - "targets": [ { "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" }, - "dateTimeType": "DATETIME", - "extrapolate": true, - "format": "table", - "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", - "intervalFactor": 1, - "query": "SELECT * FROM default.trivysbom", - "rawQuery": "SELECT * FROM default.trivysbom", - "refId": "A", - "round": "0s", - "skip_comments": true + "description": "This panel displays the count of vulnerabilities in different clusters and namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 146 + }, + "id": 6, + "options": { + "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Vulnerabilities\nFROM default.trivy_vul\nWHERE vul_last_modified_date >= toDateTime(1694156175) AND vul_last_modified_date <= toDateTime(1694242575)\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Vulnerability Count by Cluster and Namespace", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "description": "This panel displays the count of Misconfigurations in different clusters and namespaces.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 146 + }, + "id": 4, + "options": { + "displayMode": "lcd", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY cluster_name, namespace", + "rawQuery": "SELECT cluster_name, namespace, count(*) AS Misconfigurations\nFROM default.trivy_misconfig\nWHERE EventTime >= toDateTime(1694156545) AND EventTime <= toDateTime(1694242945)\nGROUP BY cluster_name, namespace", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Misconfiguration Count by Cluster and Namespace", + "type": "bargauge" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "noValue": "Trivy Vulnerabilities Not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 154 + }, + "id": 32, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", + "rawQuery": "SELECT \"cluster_name\", \"namespace\", \"kind\", \"name\", \"vul_id\", \"vul_vendor_ids\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_pkg_path\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivy_vul\"\nWHERE vul_last_modified_date >= toDateTime(1694099993) AND vul_last_modified_date <= toDateTime(1694186393)\nORDER BY vul_last_modified_date DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Trivy Vulnerabilities", + "type": "table" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "noValue": "Trivy Misconfigurations not available", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 162 + }, + "id": 33, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"namespace\", \"kind\", \"name\", \"misconfig_id\", \"misconfig_avdid\", \"misconfig_type\", \"misconfig_title\", \"misconfig_desc\", \"misconfig_msg\", \"misconfig_query\", \"misconfig_resolution\", \"misconfig_severity\", \"misconfig_status\" \nFROM \"default\".\"trivy_misconfig\"\nWHERE EventTime >= toDateTime(1694966455) AND EventTime <= toDateTime(1695052855)\nORDER BY EventTime DESC", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Trivy Misconfiguration", + "type": "table" } ], - "title": "Trivy_SBOM", - "type": "table" + "title": "Trivy Vulnerability and Misconfiguration", + "type": "row" } ], "refresh": "", @@ -1442,6 +2396,6 @@ "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 1, + "version": 13, "weekStart": "" } \ No newline at end of file From 5d57721faf841d7eca83e344a10af195ad1bca25 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Sat, 16 Mar 2024 13:54:26 +0530 Subject: [PATCH 235/263] Updated image tag to v1.1.5 release --- charts/agent/Chart.yaml | 4 ++-- charts/agent/values.yaml | 6 +++--- charts/client/Chart.yaml | 4 ++-- charts/client/values.yaml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 694dc262..25cca1a8 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.14 +version: 1.1.15 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.4" +appVersion: "v1.1.5" diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index f119e92a..38f991b3 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/kubviz-agent pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.4" + tag: "v1.1.5" imagePullSecrets: [] nameOverride: "" @@ -51,7 +51,7 @@ git_bridge: image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always - tag: "v1.1.4" + tag: "v1.1.5" resources: limits: cpu: 200m @@ -95,7 +95,7 @@ container_bridge: image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always - tag: "v1.1.4" + tag: "v1.1.5" resources: limits: cpu: 200m diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 4baa724f..b38b2315 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.20 +version: 1.1.21 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.4" +appVersion: "v1.1.5" dependencies: - name: nats condition: nats.enabled diff --git a/charts/client/values.yaml b/charts/client/values.yaml index d2186fd6..a6caddf7 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/client pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.4" + tag: "v1.1.5" imagePullSecrets: [] nameOverride: "" @@ -164,7 +164,7 @@ migration: image: repository: ghcr.io/intelops/kubviz/migration pullPolicy: Always - tag: "v1.1.4" + tag: "v1.1.5" schema: path: "/sql" From 1fdc4c9b863377e68a9b2152457a10d606b11ca8 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 11:36:01 +0530 Subject: [PATCH 236/263] Scheduler change for trivy --- agent/config/config.go | 22 ++--- agent/kubviz/k8smetrics_agent.go | 2 +- agent/kubviz/scheduler/scheduler.go | 26 +++++- agent/kubviz/scheduler/scheduler_watch.go | 100 +++++++++++++++++----- 4 files changed, 116 insertions(+), 34 deletions(-) diff --git a/agent/config/config.go b/agent/config/config.go index 632b3332..8e906ce1 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -8,16 +8,18 @@ import ( ) type AgentConfigurations struct { - SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` - SAName string `envconfig:"SA_NAME" default:"default"` - OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"0"` - GetAllInterval string `envconfig:"GETALL_INTERVAL" default:"*/30 * * * *"` - KubeScoreInterval string `envconfig:"KUBESCORE_INTERVAL" default:"*/40 * * * *"` - RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` - KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` - TrivyInterval string `envconfig:"TRIVY_INTERVAL" default:"*/10 * * * *"` - SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"true"` - KuberHealthyEnable bool `envconfig:"KUBERHEALTHY_ENABLE" default:"true"` + SANamespace string `envconfig:"SA_NAMESPACE" default:"default"` + SAName string `envconfig:"SA_NAME" default:"default"` + OutdatedInterval string `envconfig:"OUTDATED_INTERVAL" default:"0"` + GetAllInterval string `envconfig:"GETALL_INTERVAL" default:"*/30 * * * *"` + KubeScoreInterval string `envconfig:"KUBESCORE_INTERVAL" default:"*/40 * * * *"` + RakkessInterval string `envconfig:"RAKKESS_INTERVAL" default:"*/50 * * * *"` + KubePreUpgradeInterval string `envconfig:"KUBEPREUPGRADE_INTERVAL" default:"*/60 * * * *"` + TrivyImageInterval string `envconfig:"TRIVY_IMAGE_INTERVAL" default:"*/10 * * * *"` + TrivySbomInterval string `envconfig:"TRIVY_SBOM_INTERVAL" default:"*/20 * * * *"` + TrivyClusterScanInterval string `envconfig:"TRIVY_CLUSTERSCAN_INTERVAL" default:"*/35 * * * *"` + SchedulerEnable bool `envconfig:"SCHEDULER_ENABLE" default:"true"` + KuberHealthyEnable bool `envconfig:"KUBERHEALTHY_ENABLE" default:"true"` } func GetAgentConfigurations() (serviceConf *AgentConfigurations, err error) { diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 068360eb..f5a874f1 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -155,7 +155,7 @@ func main() { events.LogErr(err) } - collectAndPublishMetrics() + //collectAndPublishMetrics() if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) diff --git a/agent/kubviz/scheduler/scheduler.go b/agent/kubviz/scheduler/scheduler.go index f56660bc..ccd9564e 100644 --- a/agent/kubviz/scheduler/scheduler.go +++ b/agent/kubviz/scheduler/scheduler.go @@ -144,12 +144,32 @@ func InitScheduler(config *rest.Config, js nats.JetStreamContext, cfg config.Age log.Fatal("failed to do job", err) } } - if cfg.TrivyInterval != "" && cfg.TrivyInterval != "0" { - sj, err := NewTrivyJob(config, js, cfg.TrivyInterval) + if cfg.TrivyImageInterval != "" && cfg.TrivyImageInterval != "0" { + sj, err := NewTrivyImagesJob(config, js, cfg.TrivyImageInterval) if err != nil { log.Fatal("no time interval", err) } - err = s.AddJob("Trivy", sj) + err = s.AddJob("Trivyimage", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.TrivySbomInterval != "" && cfg.TrivySbomInterval != "0" { + sj, err := NewTrivySbomJob(config, js, cfg.TrivySbomInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Trivysbom", sj) + if err != nil { + log.Fatal("failed to do job", err) + } + } + if cfg.TrivyClusterScanInterval != "" && cfg.TrivyClusterScanInterval != "0" { + sj, err := NewTrivyClusterScanJob(js, cfg.TrivyClusterScanInterval) + if err != nil { + log.Fatal("no time interval", err) + } + err = s.AddJob("Trivycluster", sj) if err != nil { log.Fatal("failed to do job", err) } diff --git a/agent/kubviz/scheduler/scheduler_watch.go b/agent/kubviz/scheduler/scheduler_watch.go index 9b338cf7..cadeb462 100644 --- a/agent/kubviz/scheduler/scheduler_watch.go +++ b/agent/kubviz/scheduler/scheduler_watch.go @@ -25,11 +25,21 @@ type KetallJob struct { js nats.JetStreamContext frequency string } -type TrivyJob struct { +type TrivyImageJob struct { config *rest.Config js nats.JetStreamContext frequency string } +type TrivySbomJob struct { + config *rest.Config + js nats.JetStreamContext + frequency string +} +type TrivyClusterScanJob struct { + //config *rest.Config + js nats.JetStreamContext + frequency string +} type RakkessJob struct { config *rest.Config js nats.JetStreamContext @@ -46,6 +56,55 @@ type KubescoreJob struct { frequency string } +func NewTrivySbomJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivySbomJob, error) { + return &TrivySbomJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *TrivySbomJob) CronSpec() string { + return v.frequency +} + +func (j *TrivySbomJob) Run() { + // Call the outDatedImages function with the provided config and js + err := trivy.RunTrivySbomScan(j.config, j.js) + events.LogErr(err) +} + +func NewTrivyClusterScanJob(js nats.JetStreamContext, frequency string) (*TrivyClusterScanJob, error) { + return &TrivyClusterScanJob{ + // config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *TrivyClusterScanJob) CronSpec() string { + return v.frequency +} + +func (j *TrivyClusterScanJob) Run() { + // Call the outDatedImages function with the provided config and js + err := trivy.RunTrivyK8sClusterScan(j.js) + events.LogErr(err) +} +func NewTrivyImagesJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivyImageJob, error) { + return &TrivyImageJob{ + config: config, + js: js, + frequency: frequency, + }, nil +} +func (v *TrivyImageJob) CronSpec() string { + return v.frequency +} + +func (j *TrivyImageJob) Run() { + // Call the outDatedImages function with the provided config and js + err := trivy.RunTrivyImageScans(j.config, j.js) + events.LogErr(err) +} func NewOutDatedImagesJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*OutDatedImagesJob, error) { return &OutDatedImagesJob{ config: config, @@ -128,23 +187,24 @@ func (j *RakkessJob) Run() { err := rakkess.RakeesOutput(j.config, j.js) events.LogErr(err) } -func NewTrivyJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivyJob, error) { - return &TrivyJob{ - config: config, - js: js, - frequency: frequency, - }, nil -} -func (v *TrivyJob) CronSpec() string { - return v.frequency -} -func (j *TrivyJob) Run() { - // Call the Trivy function with the provided config and js - err := trivy.RunTrivySbomScan(j.config, j.js) - events.LogErr(err) - err = trivy.RunTrivyImageScans(j.config, j.js) - events.LogErr(err) - err = trivy.RunTrivyK8sClusterScan(j.js) - events.LogErr(err) -} +// func NewTrivyJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivyJob, error) { +// return &TrivyJob{ +// config: config, +// js: js, +// frequency: frequency, +// }, nil +// } +// func (v *TrivyJob) CronSpec() string { +// return v.frequency +// } + +// func (j *TrivyJob) Run() { +// // Call the Trivy function with the provided config and js +// err := trivy.RunTrivySbomScan(j.config, j.js) +// events.LogErr(err) +// err = trivy.RunTrivyImageScans(j.config, j.js) +// events.LogErr(err) +// err = trivy.RunTrivyK8sClusterScan(j.js) +// events.LogErr(err) +// } From 7d603db5ba5026f8ecccb79a66889d81f2aad02d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 12:35:55 +0530 Subject: [PATCH 237/263] Scheduler change for trivy --- agent/kubviz/k8smetrics_agent.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index f5a874f1..c3dbc670 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -155,8 +155,6 @@ func main() { events.LogErr(err) } - //collectAndPublishMetrics() - if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) From c513f4110301bcac301a8a1014f044dde77b7d9a Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 12:40:16 +0530 Subject: [PATCH 238/263] Scheduler change for trivy --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index c3dbc670..9eb16068 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -67,7 +67,7 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - clusterMetricsChan := make(chan error, 1) + //clusterMetricsChan := make(chan error, 1) cfg, err := config.GetAgentConfigurations() if err != nil { log.Fatal("Failed to retrieve agent configurations", err) @@ -131,7 +131,7 @@ func main() { } }() - go events.PublishMetrics(clientset, js, clusterMetricsChan) + //go events.PublishMetrics(clientset, js, clusterMetricsChan) if cfg.KuberHealthyEnable { go kuberhealthy.StartKuberHealthy(js) } From 8875ffc8f7cf3546fe2f9db6bfa3824301ecc0f0 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 12:51:45 +0530 Subject: [PATCH 239/263] Scheduler change for trivy --- agent/kubviz/k8smetrics_agent.go | 56 +++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 9eb16068..854db18e 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -136,24 +136,24 @@ func main() { go kuberhealthy.StartKuberHealthy(js) } go server.StartServer() - collectAndPublishMetrics := func() { - err := outdated.OutDatedImages(config, js) - events.LogErr(err) - err = kubepreupgrade.KubePreUpgradeDetector(config, js) - events.LogErr(err) - err = ketall.GetAllResources(config, js) - events.LogErr(err) - err = rakkess.RakeesOutput(config, js) - events.LogErr(err) - err = trivy.RunTrivySbomScan(config, js) - events.LogErr(err) - err = trivy.RunTrivyImageScans(config, js) - events.LogErr(err) - err = trivy.RunTrivyK8sClusterScan(js) - events.LogErr(err) - err = kubescore.RunKubeScore(clientset, js) - events.LogErr(err) - } + // collectAndPublishMetrics := func() { + // err := outdated.OutDatedImages(config, js) + // events.LogErr(err) + // err = kubepreupgrade.KubePreUpgradeDetector(config, js) + // events.LogErr(err) + // err = ketall.GetAllResources(config, js) + // events.LogErr(err) + // err = rakkess.RakeesOutput(config, js) + // events.LogErr(err) + // err = trivy.RunTrivySbomScan(config, js) + // events.LogErr(err) + // err = trivy.RunTrivyImageScans(config, js) + // events.LogErr(err) + // err = trivy.RunTrivyK8sClusterScan(js) + // events.LogErr(err) + // err = kubescore.RunKubeScore(clientset, js) + // events.LogErr(err) + // } if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) @@ -175,8 +175,26 @@ func main() { } s := gocron.NewScheduler(time.UTC) s.Every(schedulingInterval).Do(func() { - collectAndPublishMetrics() + collectAndPublishMetrics(config, js, clientset) }) s.StartBlocking() } } +func collectAndPublishMetrics(config *rest.Config, js nats.JetStreamContext, clientset *kubernetes.Clientset) { + err := outdated.OutDatedImages(config, js) + events.LogErr(err) + err = kubepreupgrade.KubePreUpgradeDetector(config, js) + events.LogErr(err) + err = ketall.GetAllResources(config, js) + events.LogErr(err) + err = rakkess.RakeesOutput(config, js) + events.LogErr(err) + err = trivy.RunTrivySbomScan(config, js) + events.LogErr(err) + err = trivy.RunTrivyImageScans(config, js) + events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(js) + events.LogErr(err) + err = kubescore.RunKubeScore(clientset, js) + events.LogErr(err) +} From 8ccf8ea0d14dc69d3ae266c341dbdb3d525009f0 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 13:01:32 +0530 Subject: [PATCH 240/263] Scheduler change for trivy --- agent/kubviz/k8smetrics_agent.go | 75 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 854db18e..ca32d28e 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -136,24 +136,24 @@ func main() { go kuberhealthy.StartKuberHealthy(js) } go server.StartServer() - // collectAndPublishMetrics := func() { - // err := outdated.OutDatedImages(config, js) - // events.LogErr(err) - // err = kubepreupgrade.KubePreUpgradeDetector(config, js) - // events.LogErr(err) - // err = ketall.GetAllResources(config, js) - // events.LogErr(err) - // err = rakkess.RakeesOutput(config, js) - // events.LogErr(err) - // err = trivy.RunTrivySbomScan(config, js) - // events.LogErr(err) - // err = trivy.RunTrivyImageScans(config, js) - // events.LogErr(err) - // err = trivy.RunTrivyK8sClusterScan(js) - // events.LogErr(err) - // err = kubescore.RunKubeScore(clientset, js) - // events.LogErr(err) - // } + collectAndPublishMetrics := func() { + err := outdated.OutDatedImages(config, js) + events.LogErr(err) + err = kubepreupgrade.KubePreUpgradeDetector(config, js) + events.LogErr(err) + err = ketall.GetAllResources(config, js) + events.LogErr(err) + err = rakkess.RakeesOutput(config, js) + events.LogErr(err) + err = trivy.RunTrivySbomScan(config, js) + events.LogErr(err) + err = trivy.RunTrivyImageScans(config, js) + events.LogErr(err) + err = trivy.RunTrivyK8sClusterScan(js) + events.LogErr(err) + err = kubescore.RunKubeScore(clientset, js) + events.LogErr(err) + } if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) @@ -175,26 +175,27 @@ func main() { } s := gocron.NewScheduler(time.UTC) s.Every(schedulingInterval).Do(func() { - collectAndPublishMetrics(config, js, clientset) + collectAndPublishMetrics() }) s.StartBlocking() } } -func collectAndPublishMetrics(config *rest.Config, js nats.JetStreamContext, clientset *kubernetes.Clientset) { - err := outdated.OutDatedImages(config, js) - events.LogErr(err) - err = kubepreupgrade.KubePreUpgradeDetector(config, js) - events.LogErr(err) - err = ketall.GetAllResources(config, js) - events.LogErr(err) - err = rakkess.RakeesOutput(config, js) - events.LogErr(err) - err = trivy.RunTrivySbomScan(config, js) - events.LogErr(err) - err = trivy.RunTrivyImageScans(config, js) - events.LogErr(err) - err = trivy.RunTrivyK8sClusterScan(js) - events.LogErr(err) - err = kubescore.RunKubeScore(clientset, js) - events.LogErr(err) -} + +// func collectAndPublishMetrics(config *rest.Config, js nats.JetStreamContext, clientset *kubernetes.Clientset) { +// err := outdated.OutDatedImages(config, js) +// events.LogErr(err) +// err = kubepreupgrade.KubePreUpgradeDetector(config, js) +// events.LogErr(err) +// err = ketall.GetAllResources(config, js) +// events.LogErr(err) +// err = rakkess.RakeesOutput(config, js) +// events.LogErr(err) +// err = trivy.RunTrivySbomScan(config, js) +// events.LogErr(err) +// err = trivy.RunTrivyImageScans(config, js) +// events.LogErr(err) +// err = trivy.RunTrivyK8sClusterScan(js) +// events.LogErr(err) +// err = kubescore.RunKubeScore(clientset, js) +// events.LogErr(err) +// } From 853bfe04e0858d669eaeecbc97e2e6b934adfc0a Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 13:20:49 +0530 Subject: [PATCH 241/263] Scheduler change for trivy --- agent/kubviz/k8smetrics_agent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index ca32d28e..72e5703c 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -154,7 +154,7 @@ func main() { err = kubescore.RunKubeScore(clientset, js) events.LogErr(err) } - + // collectAndPublishMetrics() if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) From 4b4f10bb5f254be6fbe68d97f6361f52f2189d8b Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 18 Mar 2024 13:30:14 +0530 Subject: [PATCH 242/263] Scheduler change for trivy --- charts/agent/Chart.yaml | 2 +- charts/agent/templates/deployment.yaml | 8 ++++++-- charts/agent/values.yaml | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 25cca1a8..296eb7b5 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.15 +version: 1.1.16 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/templates/deployment.yaml b/charts/agent/templates/deployment.yaml index db0c1603..675ff272 100644 --- a/charts/agent/templates/deployment.yaml +++ b/charts/agent/templates/deployment.yaml @@ -79,8 +79,12 @@ spec: value: "{{ .Values.schedule.rakkessInterval }}" - name: KUBEPREUPGRADE_INTERVAL value: "{{ .Values.schedule.kubepreupgradeInterval }}" - - name: TRIVY_INTERVAL - value: "{{ .Values.schedule.trivyInterval }}" + - name: TRIVY_IMAGE_INTERVAL + value: "{{ .Values.schedule.trivyimageInterval }}" + - name: TRIVY_SBOM_INTERVAL + value: "{{ .Values.schedule.trivysbomInterval }}" + - name: TRIVY_CLUSTERSCAN_INTERVAL + value: "{{ .Values.schedule.trivyclusterscanInterval }}" - name: IS_OPTEL_ENABLED value: "{{ .Values.opentelemetry.isEnabled }}" - name : OPTEL_URL diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 38f991b3..6591ea22 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -184,7 +184,9 @@ schedule: kubescoreInterval: "@every 20h" rakkessInterval: "@every 21h" kubepreupgradeInterval: "@every 22h" - trivyInterval: "@every 24h" + trivyimageInterval: "@every 24h" + trivysbomInterval: "@every 16h" + trivyclusterscanInterval: "@every 17h" kuberhealthy: enabled: true From 74b6de5b44d03b0325e23568b28f0cd6f471abd3 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 25 Mar 2024 10:06:16 +0530 Subject: [PATCH 243/263] changes --- agent/kubviz/k8smetrics_agent.go | 21 +-------------------- agent/kubviz/scheduler/scheduler_watch.go | 21 --------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index 72e5703c..e31e685b 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -154,7 +154,7 @@ func main() { err = kubescore.RunKubeScore(clientset, js) events.LogErr(err) } - // collectAndPublishMetrics() + collectAndPublishMetrics() if cfg.SchedulerEnable { // Assuming "cfg.Schedule" is a boolean indicating whether to schedule or not. scheduler := scheduler.InitScheduler(config, js, *cfg, clientset) @@ -180,22 +180,3 @@ func main() { s.StartBlocking() } } - -// func collectAndPublishMetrics(config *rest.Config, js nats.JetStreamContext, clientset *kubernetes.Clientset) { -// err := outdated.OutDatedImages(config, js) -// events.LogErr(err) -// err = kubepreupgrade.KubePreUpgradeDetector(config, js) -// events.LogErr(err) -// err = ketall.GetAllResources(config, js) -// events.LogErr(err) -// err = rakkess.RakeesOutput(config, js) -// events.LogErr(err) -// err = trivy.RunTrivySbomScan(config, js) -// events.LogErr(err) -// err = trivy.RunTrivyImageScans(config, js) -// events.LogErr(err) -// err = trivy.RunTrivyK8sClusterScan(js) -// events.LogErr(err) -// err = kubescore.RunKubeScore(clientset, js) -// events.LogErr(err) -// } diff --git a/agent/kubviz/scheduler/scheduler_watch.go b/agent/kubviz/scheduler/scheduler_watch.go index cadeb462..f2e769af 100644 --- a/agent/kubviz/scheduler/scheduler_watch.go +++ b/agent/kubviz/scheduler/scheduler_watch.go @@ -187,24 +187,3 @@ func (j *RakkessJob) Run() { err := rakkess.RakeesOutput(j.config, j.js) events.LogErr(err) } - -// func NewTrivyJob(config *rest.Config, js nats.JetStreamContext, frequency string) (*TrivyJob, error) { -// return &TrivyJob{ -// config: config, -// js: js, -// frequency: frequency, -// }, nil -// } -// func (v *TrivyJob) CronSpec() string { -// return v.frequency -// } - -// func (j *TrivyJob) Run() { -// // Call the Trivy function with the provided config and js -// err := trivy.RunTrivySbomScan(j.config, j.js) -// events.LogErr(err) -// err = trivy.RunTrivyImageScans(j.config, j.js) -// events.LogErr(err) -// err = trivy.RunTrivyK8sClusterScan(j.js) -// events.LogErr(err) -// } From 2eb36a6aea1fec4ef39896ec254c7618d5f5fe9d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Mon, 25 Mar 2024 15:49:45 +0530 Subject: [PATCH 244/263] fix --- agent/kubviz/k8smetrics_agent.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/kubviz/k8smetrics_agent.go b/agent/kubviz/k8smetrics_agent.go index e31e685b..d0c0c7f6 100644 --- a/agent/kubviz/k8smetrics_agent.go +++ b/agent/kubviz/k8smetrics_agent.go @@ -67,7 +67,7 @@ var ( func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) env := Production - //clusterMetricsChan := make(chan error, 1) + clusterMetricsChan := make(chan error, 1) cfg, err := config.GetAgentConfigurations() if err != nil { log.Fatal("Failed to retrieve agent configurations", err) @@ -131,7 +131,7 @@ func main() { } }() - //go events.PublishMetrics(clientset, js, clusterMetricsChan) + go events.PublishMetrics(clientset, js, clusterMetricsChan) if cfg.KuberHealthyEnable { go kuberhealthy.StartKuberHealthy(js) } From fcc05bf69330c7e89e079db1e637352433d85e5a Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 26 Mar 2024 11:50:30 +0530 Subject: [PATCH 245/263] sbom-resolve --- agent/kubviz/plugins/trivy/trivy_sbom.go | 129 ++++++++++++++++------- client/pkg/clickhouse/db_client.go | 76 +++++++++++-- client/pkg/clickhouse/statements.go | 2 +- client/pkg/clients/kubviz_client.go | 2 +- model/trivy_sbom.go | 21 +--- sql/0000015_trivysbom.up.sql | 13 +-- 6 files changed, 166 insertions(+), 77 deletions(-) diff --git a/agent/kubviz/plugins/trivy/trivy_sbom.go b/agent/kubviz/plugins/trivy/trivy_sbom.go index 08358c32..62027761 100644 --- a/agent/kubviz/plugins/trivy/trivy_sbom.go +++ b/agent/kubviz/plugins/trivy/trivy_sbom.go @@ -8,58 +8,42 @@ import ( "log" "os" "os/exec" + "strings" - "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" "github.com/google/uuid" - "github.com/intelops/kubviz/agent/kubviz/plugins/outdated" "github.com/intelops/kubviz/constants" "github.com/intelops/kubviz/model" "github.com/intelops/kubviz/pkg/opentelemetry" "github.com/nats-io/nats.go" + "github.com/pkg/errors" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) -func PublishTrivySbomReport(report cyclonedx.BOM, js nats.JetStreamContext) error { - - for _, packageinfo := range report.Packages { - for _, pkg := range packageinfo.Packages { - - metrics := model.SbomData{ - ID: uuid.New().String(), - ClusterName: ClusterName, - ComponentName: report.CycloneDX.Metadata.Component.Name, - PackageName: pkg.Name, - PackageUrl: report.CycloneDX.Metadata.Component.PackageURL, - BomRef: report.CycloneDX.Metadata.Component.BOMRef, - SerialNumber: report.CycloneDX.SerialNumber, - CycloneDxVersion: report.CycloneDX.Version, - BomFormat: report.CycloneDX.BOMFormat, - } - metricsJson, err := json.Marshal(metrics) - if err != nil { - log.Println("error occurred while marshalling sbom metrics in agent", err.Error()) - return err - } - _, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) - if err != nil { - return err - } - log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) - } +func PublishTrivySbomReport(report map[string]interface{}, js nats.JetStreamContext) error { + + metrics := model.Sbom{ + ID: uuid.New().String(), + ClusterName: ClusterName, + Report: report, + } + metricsJson, err := json.Marshal(metrics) + if err != nil { + log.Println("error occurred while marshalling sbom metrics in agent", err.Error()) + return err + } + _, err = js.Publish(constants.TRIVY_SBOM_SUBJECT, metricsJson) + if err != nil { + return err } + log.Printf("Trivy sbom report with Id %v has been published\n", metrics.ID) return nil } func executeCommandSbom(command string) ([]byte, error) { - ctx := context.Background() - tracer := otel.Tracer("trivy-sbom") - _, span := tracer.Start(opentelemetry.BuildContext(ctx), "executeCommandSbom") - span.SetAttributes(attribute.String("trivy-sbom-agent", "sbom-command-running")) - defer span.End() - cmd := exec.Command("/bin/sh", "-c", command) var outc, errc bytes.Buffer cmd.Stdout = &outc @@ -84,10 +68,9 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { ctx := context.Background() tracer := otel.Tracer("trivy-sbom") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "RunTrivySbomScan") - span.SetAttributes(attribute.String("sbom", "sbom-creation")) defer span.End() - images, err := outdated.ListImages(config) + images, err := ListImagesforSbom(config) if err != nil { log.Printf("failed to list images: %v", err) @@ -111,13 +94,79 @@ func RunTrivySbomScan(config *rest.Config, js nats.JetStreamContext) error { continue // Move on to the next image } - var report cyclonedx.BOM + var report map[string]interface{} err = json.Unmarshal(out, &report) if err != nil { log.Printf("Error unmarshaling JSON data for image sbom %s: %v", image.PullableImage, err) continue // Move on to the next image in case of an error } - PublishTrivySbomReport(report, js) + err = PublishTrivySbomReport(report, js) + if err != nil { + log.Printf("Error publishing Trivy SBOM report for image %s: %v", image.PullableImage, err) + continue + } } return nil } + +func ListImagesforSbom(config *rest.Config) ([]model.RunningImage, error) { + var err error + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, errors.Wrap(err, "failed to create clientset") + } + ctx := context.Background() + namespaces, err := clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, errors.Wrap(err, "failed to list namespaces") + } + + runningImages := []model.RunningImage{} + for _, namespace := range namespaces.Items { + pods, err := clientset.CoreV1().Pods(namespace.Name).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, errors.Wrap(err, "failed to list pods") + } + + for _, pod := range pods.Items { + for _, initContainerStatus := range pod.Status.InitContainerStatuses { + pullable := initContainerStatus.ImageID + pullable = strings.TrimPrefix(pullable, "docker-pullable://") + runningImage := model.RunningImage{ + Pod: pod.Name, + Namespace: pod.Namespace, + InitContainer: &initContainerStatus.Name, + Image: initContainerStatus.Image, + PullableImage: pullable, + } + runningImages = append(runningImages, runningImage) + } + + for _, containerStatus := range pod.Status.ContainerStatuses { + pullable := containerStatus.ImageID + pullable = strings.TrimPrefix(pullable, "docker-pullable://") + + runningImage := model.RunningImage{ + Pod: pod.Name, + Namespace: pod.Namespace, + Container: &containerStatus.Name, + Image: containerStatus.Image, + PullableImage: pullable, + } + runningImages = append(runningImages, runningImage) + } + } + } + + // Remove exact duplicates + cleanedImages := []model.RunningImage{} + seenImages := make(map[string]bool) + for _, runningImage := range runningImages { + if !seenImages[runningImage.PullableImage] { + cleanedImages = append(cleanedImages, runningImage) + seenImages[runningImage.PullableImage] = true + } + } + + return cleanedImages, nil +} diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 284b359a..065631f6 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -38,7 +38,7 @@ type DBInterface interface { InsertGitEvent(string) InsertKubeScoreMetrics(model.KubeScoreRecommendations) InsertTrivyImageMetrics(metrics model.TrivyImage) - InsertTrivySbomMetrics(metrics model.SbomData) + InsertTrivySbomMetrics(metrics model.Sbom) InsertTrivyMetrics(metrics model.Trivy) RetriveKetallEvent() ([]model.Resource, error) RetriveOutdatedEvent() ([]model.CheckResultfinal, error) @@ -841,11 +841,10 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { } } -func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { +func (c *DBClient) InsertTrivySbomMetrics(metrics model.Sbom) { ctx := context.Background() tracer := otel.Tracer("insert-trivy-sbom") _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertTrivySbomMetrics") - span.SetAttributes(attribute.String("trivy-sbom-client", "insert")) defer span.End() tx, err := c.conn.Begin() @@ -857,16 +856,73 @@ func (c *DBClient) InsertTrivySbomMetrics(metrics model.SbomData) { log.Fatalf("error preparing statement: %v", err) } + data := metrics.Report + bomFormat, _ := data["bomFormat"].(string) //CycloneDX + serialNumber, _ := data["serialNumber"].(string) // exmplvalue:urn:uuid:146625a5-531a-40fa-a205-174448c6c569 + + // fetching metadata + metadata, ok := data["metadata"].(map[string]interface{}) + if !ok { + log.Println("error: metadata not found or not in expected format") + return + } + + // inside metadata + // taking component + component, ok := metadata["component"].(map[string]interface{}) + if !ok { + log.Println("error: component not found or not in expected format") + return + } + //timestamp, _ := metadata["timestamp"].(time.Time) + var eventTime time.Time + rawTimestamp, ok := metadata["timestamp"].(string) + if !ok { + log.Println("error: timestamp not found or not in expected format") + return + } + eventTime, err = time.Parse(time.RFC3339, rawTimestamp) + if err != nil { + log.Println("error parsing timestamp:", err) + return + } + // inside metadata + // taking component + // inside component taking bomRef, componentType, componentName, packageURL + bomRef, _ := component["bom-ref"].(string) //pkg:oci/redis@sha256:873c49204b64258778a1f34d23a962de526021e9a63b09236d6d7c86e2dd13e9?repository_url=public.ecr.aws%2Fdocker%2Flibrary%2Fredis\u0026arch=amd64 + componentType, _ := component["type"].(string) //container + componentName, _ := component["name"].(string) //public.ecr.aws/docker/library/redis@sha256:873c49204b64258778a1f34d23a962de526021e9a63b09236d6d7c86e2dd13e9 + packageURL, _ := component["purl"].(string) //pkg:oci/redis@sha256:873c49204b64258778a1f34d23a962de526021e9a63b09236d6d7c86e2dd13e9?repository_url=public.ecr.aws%2Fdocker%2Flibrary%2Fredis\u0026arch=amd64 + // fetching other componets + Components, ok := data["components"].([]interface{}) + if !ok { + log.Println("error: components not found or not in expected format") + } + var otherComponentName string + // Iterate over the components to find the desired name + for _, otherComponent := range Components { + componentsMap, ok := otherComponent.(map[string]interface{}) + if !ok { + log.Println("error: component not in expected format") + continue + } + if name, ok := componentsMap["name"].(string); ok { + otherComponentName = name // alpine + break + } + } + if _, err := stmt.Exec( metrics.ID, metrics.ClusterName, - metrics.ComponentName, - metrics.PackageName, - metrics.PackageUrl, - metrics.BomRef, - metrics.SerialNumber, - int32(metrics.CycloneDxVersion), - metrics.BomFormat, + bomFormat, + serialNumber, + bomRef, + componentName, + componentType, + packageURL, + eventTime, + otherComponentName, ); err != nil { log.Fatal(err) } diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index a728ceec..22406fbe 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -230,7 +230,7 @@ const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespac const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, image_name, package_name, package_url, bom_ref, serial_number, version, bom_format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, bom_format, serial_number, bom_ref, image_name, component_type, package_url, event_time, other_component_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertKuberhealthy string = "INSERT INTO kuberhealthy (CurrentUUID, CheckName, OK, Errors, RunDuration, Namespace, Node, LastRun, AuthoritativePod) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" \ No newline at end of file diff --git a/client/pkg/clients/kubviz_client.go b/client/pkg/clients/kubviz_client.go index b126aaa7..2e83a8b5 100644 --- a/client/pkg/clients/kubviz_client.go +++ b/client/pkg/clients/kubviz_client.go @@ -151,7 +151,7 @@ func (n *NATSContext) SubscribeAllKubvizNats(conn clickhouse.DBInterface) { Consumer: cfg.TrivySbomConsumer, Handler: func(msg *nats.Msg) { msg.Ack() - var metrics model.SbomData + var metrics model.Sbom err := json.Unmarshal(msg.Data, &metrics) if err != nil { log.Println("failed to unmarshal from nats", err) diff --git a/model/trivy_sbom.go b/model/trivy_sbom.go index 647e34a4..e763418c 100644 --- a/model/trivy_sbom.go +++ b/model/trivy_sbom.go @@ -1,24 +1,7 @@ package model -import ( - "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" -) - type Sbom struct { - ID string - Report cyclonedx.BOM -} - -type SbomData struct { - ID string + ID string ClusterName string - ComponentName string - PackageName string - PackageUrl string - BomRef string - SerialNumber string - CycloneDxVersion int - BomFormat string + Report map[string]interface{} } - - diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index 7bc749ac..2a56f678 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -1,13 +1,14 @@ CREATE TABLE IF NOT EXISTS trivysbom ( id UUID, - cluster_name String, + cluster_name String, + bom_format String, + serial_number String, + bom_ref String, image_name String, - package_name String, + componet_type String, package_url String, - bom_ref String, - serial_number String, - version INTEGER, - bom_format String, + event_time DateTime('UTC'), + other_component_name String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() From d4d90ed12b3874533a458fd6c16802b28eaa0073 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Tue, 26 Mar 2024 12:51:34 +0530 Subject: [PATCH 246/263] sbom-resolve1 --- sql/0000015_trivysbom.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index 2a56f678..d93aa1bb 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS trivysbom ( serial_number String, bom_ref String, image_name String, - componet_type String, + component_type String, package_url String, event_time DateTime('UTC'), other_component_name String, From cbfe71e7d917eeefeea4e43ad3364a0cf676b85d Mon Sep 17 00:00:00 2001 From: alanjino Date: Wed, 3 Apr 2024 18:28:05 +0530 Subject: [PATCH 247/263] pre-commit implementation Signed-off-by: alanjino --- .github/workflows/agent-container-pr.yml | 10 +-- .github/workflows/agent-container.yml | 8 +- .github/workflows/agent-git-pr.yml | 8 +- .github/workflows/agent-git.yml | 8 +- .github/workflows/agent-kubviz-image.yml | 8 +- .github/workflows/agent-kubviz-pr.yml | 8 +- .github/workflows/apisec-scan.yml | 4 +- .github/workflows/client-image.yml | 8 +- .github/workflows/client-pr.yml | 10 +-- .github/workflows/codeql.yml | 6 +- .github/workflows/devskim.yml | 2 +- .github/workflows/makefile.yml | 8 +- .github/workflows/neuralegion.yml | 4 +- .github/workflows/scorecards.yml | 8 +- .github/workflows/sonarcloud.yml | 16 ++-- .github/workflows/soos-dast-scan.yml | 2 +- .github/workflows/synopsys-io.yml | 16 ++-- .github/workflows/sysdig-scan.yml | 4 +- .github/workflows/tfsec.yml | 6 +- .pre-commit-config.yaml | 87 +++++++++++++++++++ README.md | 24 ++--- agent/container/main.go | 2 +- agent/container/openapi.yaml | 2 +- .../container/pkg/application/application.go | 2 +- agent/container/pkg/application/handlers.go | 2 +- agent/container/pkg/handler/api_handler.go | 2 +- .../container/pkg/handler/azure_container.go | 2 +- .../pkg/handler/docker_event_dockerhub.go | 2 +- .../container/pkg/handler/jfrog_container.go | 2 +- agent/container/pkg/handler/quay_handler.go | 2 +- agent/git/pkg/application/application.go | 2 +- agent/git/pkg/application/handlers.go | 6 +- agent/server/server.go | 2 +- charts/client/Chart.yaml | 2 +- .../configmap-vertamedia-datasource.yaml | 4 +- charts/client/values.yaml | 4 +- client/main.go | 2 +- client/pkg/clients/bridge_client.go | 2 +- client/pkg/clients/container_client.go | 2 +- docs/CONFIGURATION.md | 6 +- docs/CONTRIBUTING.md | 2 +- sql/0000010_trivy_misconfig.up.sql | 4 +- sql/0000011_trivyimage.up.sql | 4 +- sql/0000012_dockerhubbuild.up.sql | 4 +- sql/0000013_azurecontainerpush.up.sql | 4 +- sql/0000014_quaycontainerpush.up.sql | 4 +- sql/0000015_trivysbom.up.sql | 4 +- sql/0000016_azure_devops.up.sql | 4 +- sql/0000017_github.up.sql | 4 +- sql/0000018_gitlab.up.sql | 4 +- sql/0000019_bitbucket.up.sql | 4 +- sql/000001_events.up.sql | 4 +- sql/0000020_gitea.up.sql | 4 +- sql/000002_rakkess.up.sql | 4 +- sql/000003_DeprecatedAPIs.up.sql | 4 +- sql/000004_DeletedAPIs.up.sql | 4 +- sql/000005_jfrogcontainerpush.up.sql | 4 +- sql/000006_getall_resources.up.sql | 4 +- sql/000007_outdated_images.up.sql | 4 +- sql/000008_kubescore.up.sql | 4 +- sql/000009_trivy_vul.up.sql | 4 +- 61 files changed, 237 insertions(+), 150 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/agent-container-pr.yml b/.github/workflows/agent-container-pr.yml index 5c61e536..035fe9bd 100644 --- a/.github/workflows/agent-container-pr.yml +++ b/.github/workflows/agent-container-pr.yml @@ -17,11 +17,11 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - + - name: Set up QEMU uses: docker/setup-qemu-action@v2 - + - uses: docker/setup-buildx-action@v1 name: Set up Docker Buildx @@ -32,8 +32,8 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - + + - name: Build and push on PR uses: docker/build-push-action@v4 @@ -45,4 +45,4 @@ jobs: tags: ${{ env.REGISTRY }}/${{ github.repository }}/container-agent:pr-${{ github.event.pull_request.number }} build-args: | "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" - + diff --git a/.github/workflows/agent-container.yml b/.github/workflows/agent-container.yml index cf023c6f..d560d873 100644 --- a/.github/workflows/agent-container.yml +++ b/.github/workflows/agent-container.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout GitHub Action uses: actions/checkout@v3 - + - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v2 @@ -56,7 +56,7 @@ jobs: file: ./dockerfiles/agent/container/Dockerfile tags: ${{ env.REGISTRY }}/${{ github.repository }}/container-agent:${{ github.run_id }} labels: ${{ steps.metadata.outputs.labels }} - + push: true - name: Install cosign @@ -67,12 +67,12 @@ jobs: cosign sign -y ${{ env.REGISTRY }}/${{ github.repository }}/container-agent:${{ github.run_id }} env: COSIGN_EXPERIMENTAL: 1 - + - name: Verify the pushed tags run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/container-agent:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/agent-container.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com env: COSIGN_EXPERIMENTAL: 1 - + - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph uses: aquasecurity/trivy-action@master with: diff --git a/.github/workflows/agent-git-pr.yml b/.github/workflows/agent-git-pr.yml index cd2f294c..da62d8be 100644 --- a/.github/workflows/agent-git-pr.yml +++ b/.github/workflows/agent-git-pr.yml @@ -17,11 +17,11 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - + - name: Set up QEMU uses: docker/setup-qemu-action@v2 - + - uses: docker/setup-buildx-action@v1 name: Set up Docker Buildx @@ -32,7 +32,7 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - + - name: Build and push on PR uses: docker/build-push-action@v4 @@ -44,4 +44,4 @@ jobs: tags: ${{ env.REGISTRY }}/${{ github.repository }}/git-agent:pr-${{ github.event.pull_request.number }} build-args: | "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" - + diff --git a/.github/workflows/agent-git.yml b/.github/workflows/agent-git.yml index 657b6684..83631cd7 100644 --- a/.github/workflows/agent-git.yml +++ b/.github/workflows/agent-git.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout GitHub Action uses: actions/checkout@v3 - + - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v2 @@ -56,7 +56,7 @@ jobs: file: ./dockerfiles/agent/git/Dockerfile tags: ${{ env.REGISTRY }}/${{ github.repository }}/git-agent:${{ github.run_id }} labels: ${{ steps.metadata.outputs.labels }} - + push: true - name: Install cosign @@ -67,12 +67,12 @@ jobs: cosign sign -y ${{ env.REGISTRY }}/${{ github.repository }}/git-agent:${{ github.run_id }} env: COSIGN_EXPERIMENTAL: 1 - + - name: Verify the pushed tags run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/git-agent:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/agent-git.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com env: COSIGN_EXPERIMENTAL: 1 - + - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph uses: aquasecurity/trivy-action@master with: diff --git a/.github/workflows/agent-kubviz-image.yml b/.github/workflows/agent-kubviz-image.yml index 30586d63..44c2bb67 100644 --- a/.github/workflows/agent-kubviz-image.yml +++ b/.github/workflows/agent-kubviz-image.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout GitHub Action uses: actions/checkout@v3 - + - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v2 @@ -56,7 +56,7 @@ jobs: file: ./dockerfiles/agent/kubviz/Dockerfile tags: ${{ env.REGISTRY }}/${{ github.repository }}/kubviz-agent:${{ github.run_id }} labels: ${{ steps.metadata.outputs.labels }} - + push: true - name: Install cosign @@ -67,12 +67,12 @@ jobs: cosign sign -y ${{ env.REGISTRY }}/${{ github.repository }}/kubviz-agent:${{ github.run_id }} env: COSIGN_EXPERIMENTAL: 1 - + - name: Verify the pushed tags run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/kubviz-agent:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/agent-kubviz-image.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com env: COSIGN_EXPERIMENTAL: 1 - + - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph uses: aquasecurity/trivy-action@master with: diff --git a/.github/workflows/agent-kubviz-pr.yml b/.github/workflows/agent-kubviz-pr.yml index cc0cf561..29eef866 100644 --- a/.github/workflows/agent-kubviz-pr.yml +++ b/.github/workflows/agent-kubviz-pr.yml @@ -17,11 +17,11 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - + - name: Set up QEMU uses: docker/setup-qemu-action@v2 - + - uses: docker/setup-buildx-action@v1 name: Set up Docker Buildx @@ -32,7 +32,7 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - + - name: Build and push on PR uses: docker/build-push-action@v4 @@ -44,4 +44,4 @@ jobs: tags: ${{ env.REGISTRY }}/${{ github.repository }}/kubviz-agent:pr-${{ github.event.pull_request.number }} build-args: | "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" - + diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml index b834f854..6151d036 100644 --- a/.github/workflows/apisec-scan.yml +++ b/.github/workflows/apisec-scan.yml @@ -3,8 +3,8 @@ # separate terms of service, privacy policy, and support # documentation. -# APIsec addresses the critical need to secure APIs before they reach production. -# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. +# APIsec addresses the critical need to secure APIs before they reach production. +# APIsec provides the industry’s only automated and continuous API testing platform that uncovers security vulnerabilities and logic flaws in APIs. # Clients rely on APIsec to evaluate every update and release, ensuring that no APIs go to production with vulnerabilities. # How to Get Started with APIsec.ai diff --git a/.github/workflows/client-image.yml b/.github/workflows/client-image.yml index 77e20a1c..153881de 100644 --- a/.github/workflows/client-image.yml +++ b/.github/workflows/client-image.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout GitHub Action uses: actions/checkout@v3 - + - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v2 @@ -56,7 +56,7 @@ jobs: file: ./dockerfiles/client/Dockerfile tags: ${{ env.REGISTRY }}/${{ github.repository }}/client:${{ github.run_id }} labels: ${{ steps.metadata.outputs.labels }} - + push: true - name: Install cosign @@ -67,12 +67,12 @@ jobs: cosign sign -y ${{ env.REGISTRY }}/${{ github.repository }}/client:${{ github.run_id }} env: COSIGN_EXPERIMENTAL: 1 - + - name: Verify the pushed tags run: cosign verify ${{ env.REGISTRY }}/${{ github.repository }}/client:${{ github.run_id }} --certificate-identity ${{ env.GH_URL }}/${{ github.repository }}/.github/workflows/client-image.yml@refs/heads/main --certificate-oidc-issuer https://token.actions.githubusercontent.com env: COSIGN_EXPERIMENTAL: 1 - + - name: Run Trivy in GitHub SBOM mode and submit results to Dependency Graph uses: aquasecurity/trivy-action@master with: diff --git a/.github/workflows/client-pr.yml b/.github/workflows/client-pr.yml index 870686a1..cf312014 100644 --- a/.github/workflows/client-pr.yml +++ b/.github/workflows/client-pr.yml @@ -17,11 +17,11 @@ jobs: uses: actions/checkout@v3 with: fetch-depth: 0 - + - name: Set up QEMU uses: docker/setup-qemu-action@v2 - + - uses: docker/setup-buildx-action@v1 name: Set up Docker Buildx @@ -32,8 +32,8 @@ jobs: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - + + - name: Build and push on PR uses: docker/build-push-action@v4 @@ -45,4 +45,4 @@ jobs: tags: ${{ env.REGISTRY }}/${{ github.repository }}/client:pr-${{ github.event.pull_request.number }} build-args: | "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" - + diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 533200d3..df705353 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,11 +48,11 @@ jobs: # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. - + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild @@ -61,7 +61,7 @@ jobs: # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # If the Autobuild fails above, remove it and uncomment the following three lines. + # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | diff --git a/.github/workflows/devskim.yml b/.github/workflows/devskim.yml index 62d8407f..d1684f76 100644 --- a/.github/workflows/devskim.yml +++ b/.github/workflows/devskim.yml @@ -27,7 +27,7 @@ jobs: - name: Run DevSkim scanner uses: microsoft/DevSkim-Action@v1 - + - name: Upload DevSkim scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 with: diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index e4dadb23..8db022e5 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -13,15 +13,15 @@ jobs: steps: - uses: actions/checkout@v3 - + - name: configure run: ./configure - + - name: Install dependencies run: make - + - name: Run check run: make check - + - name: Run distcheck run: make distcheck diff --git a/.github/workflows/neuralegion.yml b/.github/workflows/neuralegion.yml index 9d130bcb..341929c3 100644 --- a/.github/workflows/neuralegion.yml +++ b/.github/workflows/neuralegion.yml @@ -50,7 +50,7 @@ # # `restart_scan` # -# **Required** when restarting an existing scan by its ID. You can get the scan ID in the Scans section on [nexploit.app](https://nexploit.app/login).
Please make sure to only use the necessary parameters. Otherwise, you will get a response with the parameter usage requirements. +# **Required** when restarting an existing scan by its ID. You can get the scan ID in the Scans section on [nexploit.app](https://nexploit.app/login).
Please make sure to only use the necessary parameters. Otherwise, you will get a response with the parameter usage requirements. # # _Example:_ `restart_scan: ai3LG8DmVn9Rn1YeqCNRGQ)` # @@ -95,7 +95,7 @@ # # `hosts_filter` # -# **Required** when the the discovery type is set to `archive`. Allows selecting specific hosts for a scan. +# **Required** when the the discovery type is set to `archive`. Allows selecting specific hosts for a scan. # # Outputs # diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 51a3db4f..b4d7cff2 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -22,7 +22,7 @@ jobs: # Needs for private repositories. contents: read actions: read - + steps: - name: "Checkout code" uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 @@ -41,8 +41,8 @@ jobs: # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} # Publish the results for public repositories to enable scorecard badges. For more details, see - # https://github.com/ossf/scorecard-action#publishing-results. - # For private repositories, `publish_results` will automatically be set to `false`, regardless + # https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories, `publish_results` will automatically be set to `false`, regardless # of the value entered here. publish_results: true @@ -54,7 +54,7 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - + # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 81c46613..e9464daa 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -3,7 +3,7 @@ # separate terms of service, privacy policy, and support # documentation. -# This workflow helps you trigger a SonarCloud analysis of your code and populates +# This workflow helps you trigger a SonarCloud analysis of your code and populates # GitHub Code Scanning alerts with the vulnerabilities found. # Free for open source project. @@ -11,16 +11,16 @@ # 2. Import your project on SonarCloud # * Add your GitHub organization first, then add your repository as a new project. -# * Please note that many languages are eligible for automatic analysis, +# * Please note that many languages are eligible for automatic analysis, # which means that the analysis will start automatically without the need to set up GitHub Actions. # * This behavior can be changed in Administration > Analysis Method. -# +# # 3. Follow the SonarCloud in-product tutorial # * a. Copy/paste the Project Key and the Organization Key into the args parameter below # (You'll find this information in SonarCloud. Click on "Information" at the bottom left) # # * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN -# (On SonarCloud, click on your avatar on top-right > My account > Security +# (On SonarCloud, click on your avatar on top-right > My account > Security # or go directly to https://sonarcloud.io/account/security/) # Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/) @@ -41,9 +41,9 @@ permissions: jobs: Analysis: runs-on: ubuntu-latest - + steps: - - name: Analyze with SonarCloud + - name: Analyze with SonarCloud # You can pin the exact commit or the version. # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 @@ -53,7 +53,7 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) with: # Additional arguments for the sonarcloud scanner - args: + args: # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) # mandatory -Dsonar.projectKey= @@ -65,4 +65,4 @@ jobs: # Comma-separated paths to directories containing test source files. #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. - #-Dsonar.verbose= # optional, default is false + #-Dsonar.verbose= # optional, default is false diff --git a/.github/workflows/soos-dast-scan.yml b/.github/workflows/soos-dast-scan.yml index 4fad66ea..5e4418ba 100644 --- a/.github/workflows/soos-dast-scan.yml +++ b/.github/workflows/soos-dast-scan.yml @@ -24,7 +24,7 @@ jobs: soos: permissions: security-events: write # for uploading code scanning alert info - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status name: SOOS DAST Scan runs-on: ubuntu-latest steps: diff --git a/.github/workflows/synopsys-io.yml b/.github/workflows/synopsys-io.yml index e578be24..59891d57 100644 --- a/.github/workflows/synopsys-io.yml +++ b/.github/workflows/synopsys-io.yml @@ -22,11 +22,11 @@ jobs: actions: read contents: read security-events: write - + steps: - name: Checkout repository uses: actions/checkout@v3 - + - name: Synopsys Intelligent Security Scan id: prescription uses: synopsys-sig/intelligent-security-scan@48eedfcd42bc342a294dc495ac452797b2d9ff08 @@ -36,7 +36,7 @@ jobs: workflowServerUrl: ${{secrets.WORKFLOW_SERVER_URL}} additionalWorkflowArgs: --polaris.url=${{secrets.POLARIS_SERVER_URL}} --polaris.token=${{secrets.POLARIS_ACCESS_TOKEN}} stage: "IO" - + # Please note that the ID in previous step was set to prescription # in order for this logic to work also make sure that POLARIS_ACCESS_TOKEN # is defined in settings @@ -48,7 +48,7 @@ jobs: wget -q ${{ secrets.POLARIS_SERVER_URL}}/api/tools/polaris_cli-linux64.zip unzip -j polaris_cli-linux64.zip -d /tmp /tmp/polaris analyze -w - + # Please note that the ID in previous step was set to prescription # in order for this logic to work - name: Software Composition Analysis with Black Duck @@ -56,7 +56,7 @@ jobs: uses: blackducksoftware/github-action@9ea442b34409737f64743781e9adc71fd8e17d38 with: args: '--blackduck.url="${{ secrets.BLACKDUCK_URL}}" --blackduck.api.token="${{ secrets.BLACKDUCK_TOKEN}}" --detect.tools="SIGNATURE_SCAN,DETECTOR"' - + - name: Synopsys Intelligent Security Scan if: ${{ steps.prescription.outputs.sastScan == 'true' || steps.prescription.outputs.scaScan == 'true' }} uses: synopsys-sig/intelligent-security-scan@48eedfcd42bc342a294dc495ac452797b2d9ff08 @@ -64,11 +64,11 @@ jobs: ioServerUrl: ${{secrets.IO_SERVER_URL}} ioServerToken: ${{secrets.IO_SERVER_TOKEN}} workflowServerUrl: ${{secrets.WORKFLOW_SERVER_URL}} - additionalWorkflowArgs: --IS_SAST_ENABLED=${{steps.prescription.outputs.sastScan}} --IS_SCA_ENABLED=${{steps.prescription.outputs.scaScan}} - --polaris.project.name={{PROJECT_NAME}} --polaris.url=${{secrets.POLARIS_SERVER_URL}} --polaris.token=${{secrets.POLARIS_ACCESS_TOKEN}} + additionalWorkflowArgs: --IS_SAST_ENABLED=${{steps.prescription.outputs.sastScan}} --IS_SCA_ENABLED=${{steps.prescription.outputs.scaScan}} + --polaris.project.name={{PROJECT_NAME}} --polaris.url=${{secrets.POLARIS_SERVER_URL}} --polaris.token=${{secrets.POLARIS_ACCESS_TOKEN}} --blackduck.project.name={{PROJECT_NAME}}:{{PROJECT_VERSION}} --blackduck.url=${{secrets.BLACKDUCK_URL}} --blackduck.api.token=${{secrets.BLACKDUCK_TOKEN}} stage: "WORKFLOW" - + - name: Upload SARIF file if: ${{steps.prescription.outputs.sastScan == 'true' }} uses: github/codeql-action/upload-sarif@v2 diff --git a/.github/workflows/sysdig-scan.yml b/.github/workflows/sysdig-scan.yml index 0a628f9b..568b8a9d 100644 --- a/.github/workflows/sysdig-scan.yml +++ b/.github/workflows/sysdig-scan.yml @@ -38,7 +38,7 @@ jobs: id: scan uses: sysdiglabs/scan-action@768d7626a14897e0948ea89c8437dd46a814b163 with: - # Tag of the image to analyse. + # Tag of the image to analyse. # Change ${{ github.repository }} variable by another image name if you want but don't forget changing also image-tag above image-tag: ${{ github.repository }}:latest # API token for Sysdig Scanning auth @@ -46,7 +46,7 @@ jobs: # Sysdig secure endpoint. Please read: https://docs.sysdig.com/en/docs/administration/saas-regions-and-ip-ranges/ # US-East https://secure.sysdig.com # US-West https://us2.app.sysdig.com - # EU https://eu1.app.sysdig.com + # EU https://eu1.app.sysdig.com sysdig-secure-url: https://us2.app.sysdig.com dockerfile-path: ./Dockerfile input-type: docker-daemon diff --git a/.github/workflows/tfsec.yml b/.github/workflows/tfsec.yml index d81c0e70..3f55eba6 100644 --- a/.github/workflows/tfsec.yml +++ b/.github/workflows/tfsec.yml @@ -9,7 +9,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] + branches: [ "main" ] schedule: - cron: '28 1 * * 6' @@ -29,10 +29,10 @@ jobs: - name: Run tfsec uses: tfsec/tfsec-sarif-action@9a83b5c3524f825c020e356335855741fd02745f with: - sarif_file: tfsec.sarif + sarif_file: tfsec.sarif - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v2 with: # Path to SARIF file relative to the root of the repository - sarif_file: tfsec.sarif + sarif_file: tfsec.sarif diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..18725306 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,87 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + # Checks for files that contain merge conflict strings. + - id: check-merge-conflict + # Detects aws credentials from the aws cli credentials file. + - id: detect-aws-credentials + args: [--allow-missing-credentials] + # detects the presence of private keys. + - id: detect-private-key + # Trims trailing whitespace in codebase. + - id: trailing-whitespace + # Protect commit to main branch + - id: no-commit-to-branch + args: [--branch,main] + + +# Check is the Commit is Signed off using `--signoff/-s` +- repo: https://github.com/KAUTH/pre-commit-git-checks + rev: v0.0.1 # Use the SHA or tag you want to point to + hooks: + - id: git-signoff + stages: [commit-msg] + +# Checks your git commit messages for style. +- repo: https://github.com/jorisroovers/gitlint + rev: v0.19.1 + hooks: + - id: gitlint + name: Scan Commit messages + +# Detects hardcoded secrets, security vulnerabilities and policy breaks using GGShield +- repo: https://github.com/zricethezav/gitleaks + rev: v8.18.1 + hooks: + - id: gitleaks + name: Detect hardcoded secrets + description: Detect hardcoded secrets using Gitleaks + entry: gitleaks protect --verbose --redact --staged + language: golang + pass_filenames: false + +- repo: https://github.com/Bahjat/pre-commit-golang + rev: v1.0.3 + hooks: + # Formats Go code + # - id: gofumpt # requires gofumpt to be installed from github.com/mvdan/gofumpt + # name: Go formatter + # description: Runs a strict Go formatter + - id: go-fmt-import + name: Go formatter + description: Go formatter with fmt and imports + # Runs Unit tests + - id: go-unit-tests + name: Run Unit tests + desription: Runs all the unit tests in the repo + # Runs static analysis of the Go code + - id: go-static-check + name: Go Static Check + description: Finds bugs and performance issues + +# Local hooks + +- repo: https://github.com/intelops/gitrepos-templates-policies + rev: v0.0.1 + hooks: + - id: check-devcontainer + name: Check devcontainer + description: Checks for existance of .devcontainer.json in the project + - id: check-dockerfile + name: Check Dockerfile + description: Enforce use of Chainguard base images in Dockefiles + - id: check-gitsign + name: Check gitsign + description: Check if the last commit is signed with Sigstore gitsign + - id: check-multistage-dockerfile + name: Check multi-stage Dockerfile + description: Check the existance of Dockerfile in the project and verify that its a multi-stage Dockerfile + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + name: Verify YAML syntax + args: + - --allow-multiple-documents diff --git a/README.md b/README.md index f9ce3923..4271ad00 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Visualize Kubernetes & DevSecOps Workflows. Tracks changes/events real-time acro ## How KubViz works -KubViz client can be installed on any Kubernetes cluster. KubViz agent runs in a kubernetes cluster where the changes/events need to be tracked. The agent detects the changes in real time and send those events via NATS JetStream and the same is received in the KubViz client. +KubViz client can be installed on any Kubernetes cluster. KubViz agent runs in a kubernetes cluster where the changes/events need to be tracked. The agent detects the changes in real time and send those events via NATS JetStream and the same is received in the KubViz client. KubViz client receives the events and passes it to Clickhouse database. The events present in the Clickhouse database can be visualized through Grafana. @@ -59,7 +59,7 @@ It comprehensively scans Kubernetes containers for security flaws, such as vulne ## How to install and run Kubviz #### Prerequisites -* A Kubernetes cluster +* A Kubernetes cluster * Helm binary #### Prepare Namespace @@ -87,10 +87,10 @@ token=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$token" ``` -**NOTE:** +**NOTE:** - If you want to get a token from a secret, use a secret reference with the secret's name and key. -**NOTE:** +**NOTE:** - If you want to enable Grafana with the client deployment, add `--set grafana.enabled=true` to the helm upgrade command. - Kubviz provides a setup for Grafana with Postgres data persistence, ensuring that even if the grafana pod/service goes down, the data will persist, safeguarding crucial information for visualization and analysis. @@ -99,7 +99,7 @@ helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$to helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$token" --set grafana.enabled=true --set grafana.postgresql=true ``` -- If grafana already exist use the same upgrade command without --set grafana.enabled=true flag. +- If grafana already exist use the same upgrade command without --set grafana.enabled=true flag. ```bash helm upgrade -i kubviz-client kubviz/client -n kubviz --set "nats.auth.token=$token" --set grafana.enabled=true @@ -130,12 +130,12 @@ kubectl get services kubviz-client-nats-external -n kubviz --output jsonpath='{. helm upgrade -i kubviz-agent kubviz/agent -n kubviz \ --set "nats.auth.token=$token" \ --set git_bridge.enabled=true \ - --set "git_bridge.ingress.hosts[0].host=",git_bridge.ingress.hosts[0].paths[0].path=/,git_bridge.ingress.hosts[0].paths[0].pathType=Prefix,git_bridge.ingress.tls[0].secretName=,git_bridge.ingress.tls[0].hosts[0]= \ + --set "git_bridge.ingress.hosts[0].host=",git_bridge.ingress.hosts[0].paths[0].path=/,git_bridge.ingress.hosts[0].paths[0].pathType=Prefix,git_bridge.ingress.tls[0].secretName=,git_bridge.ingress.tls[0].hosts[0]= \ --set container_bridge.enabled=true \ --set "container_bridge.ingress.hosts[0].host=",container_bridge.ingress.hosts[0].paths[0].path=/,container_bridge.ingress.hosts[0].paths[0].pathType=Prefix,container_bridge.ingress.tls[0].secretName=,container_bridge.ingress.tls[0].hosts[0]= ``` -**NOTE:** +**NOTE:** If you want to get a token from a secret, use a secret reference with the secret's name and key. 3. Replace "INGRESS HOSTNAME" with the desired hostname for the Git Bridge and Container Bridge Ingress configurations. @@ -155,7 +155,7 @@ Parameter | Description | Default `git_bridge.ingress.tls` | git_bridge ingress tls configuration | [] `container_bridge.ingress.tls` | container_bridge ingress tls configuration | [] -**NOTE:** +**NOTE:** - Default Annotations for Ingress @@ -180,11 +180,11 @@ helm upgrade -i kubviz-agent kubviz/agent -f values.yaml -n kubviz 1. Run the following command to deploy the KubViz agent: ```bash -helm upgrade -i kubviz-agent kubviz/agent -n kubviz --set nats.host= --set "nats.auth.token=$token" +helm upgrade -i kubviz-agent kubviz/agent -n kubviz --set nats.host= --set "nats.auth.token=$token" ``` 2. Replace "" with the IP address of your NATS service **kubviz-client-nats-external**. -**NOTE:** +**NOTE:** The time-based job scheduler is added for each plugin, allowing you to schedule and automate the execution of plugins at specific times or intervals. To activate this scheduler, set 'enabled' to 'true.' Once enabled, each plugin's execution can be configured to run at a precise time or at regular intervals, based on the provided settings. Additionally, if you set the 'schedulingInterval' to '0', it will disable the plugins. @@ -217,7 +217,7 @@ kubectl get secret --namespace kubviz kubviz-client-grafana -o jsonpath="{.data. ```bash export POD_NAME=$(kubectl get pods --namespace kubviz -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=kubviz-client" -o jsonpath="{.items[0].metadata.name}") ``` -```bash +```bash kubectl --namespace kubviz port-forward $POD_NAME 3000 ``` @@ -243,7 +243,7 @@ To guide you through the process of setting up a TTL, [please follow these steps Use KubViz to monitor your cluster events, including: -- State changes +- State changes - Errors - Other messages that occur in the cluster diff --git a/agent/container/main.go b/agent/container/main.go index 56b6958a..72f76654 100755 --- a/agent/container/main.go +++ b/agent/container/main.go @@ -23,7 +23,7 @@ func main() { log.Printf("Error shutting down tracer provider: %v", err) } }() - + app := application.New() go app.GithubContainerWatch() go app.Start() diff --git a/agent/container/openapi.yaml b/agent/container/openapi.yaml index de7b8eb2..c3c3829d 100755 --- a/agent/container/openapi.yaml +++ b/agent/container/openapi.yaml @@ -59,6 +59,6 @@ paths: summary: Post Jfrog Container Registry webhook events responses: '200': - description: OK + description: OK # oapi-codegen -config ./cfg.yaml ./openapi.yaml diff --git a/agent/container/pkg/application/application.go b/agent/container/pkg/application/application.go index 7cabbbcd..6d81a970 100755 --- a/agent/container/pkg/application/application.go +++ b/agent/container/pkg/application/application.go @@ -52,7 +52,7 @@ func New() *Application { } r.Use(otelgin.Middleware(config.ServiceName)) - + apiServer.BindRequest(r) httpServer := &http.Server{ diff --git a/agent/container/pkg/application/handlers.go b/agent/container/pkg/application/handlers.go index cca3a95d..6d94f33c 100755 --- a/agent/container/pkg/application/handlers.go +++ b/agent/container/pkg/application/handlers.go @@ -20,7 +20,7 @@ func (app *Application) localRegistryHandler(w http.ResponseWriter, r *http.Requ _, span := tracer.Start(opentelemetry.BuildContext(ctx), "localRegistryHandler") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + event, err := io.ReadAll(r.Body) if err != nil { log.Printf("Event body read failed: %v", err) diff --git a/agent/container/pkg/handler/api_handler.go b/agent/container/pkg/handler/api_handler.go index 0e6bdd01..6efee78d 100755 --- a/agent/container/pkg/handler/api_handler.go +++ b/agent/container/pkg/handler/api_handler.go @@ -39,7 +39,7 @@ func (ah *APIHandler) BindRequest(r *gin.Engine) { } r.Use(otelgin.Middleware(config.ServiceName)) - + apiGroup := r.Group("/") { apiGroup.GET("/api-docs", ah.GetApiDocs) diff --git a/agent/container/pkg/handler/azure_container.go b/agent/container/pkg/handler/azure_container.go index 5e881703..35a72f3f 100644 --- a/agent/container/pkg/handler/azure_container.go +++ b/agent/container/pkg/handler/azure_container.go @@ -26,7 +26,7 @@ func (ah *APIHandler) PostEventAzureContainer(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "PostEventAzureContainer") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/container/pkg/handler/docker_event_dockerhub.go b/agent/container/pkg/handler/docker_event_dockerhub.go index 9066c947..f74bd8ae 100644 --- a/agent/container/pkg/handler/docker_event_dockerhub.go +++ b/agent/container/pkg/handler/docker_event_dockerhub.go @@ -23,7 +23,7 @@ func (ah *APIHandler) PostEventDockerHub(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "PostEventDockerHub") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/container/pkg/handler/jfrog_container.go b/agent/container/pkg/handler/jfrog_container.go index 77b0451f..8d57f272 100644 --- a/agent/container/pkg/handler/jfrog_container.go +++ b/agent/container/pkg/handler/jfrog_container.go @@ -21,7 +21,7 @@ func (ah *APIHandler) PostEventJfrogContainer(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "PostEventJfrogContainer") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/container/pkg/handler/quay_handler.go b/agent/container/pkg/handler/quay_handler.go index b675658f..b1a2be84 100644 --- a/agent/container/pkg/handler/quay_handler.go +++ b/agent/container/pkg/handler/quay_handler.go @@ -18,7 +18,7 @@ func (ah *APIHandler) PostEventQuayContainer(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "PostEventQuayContainer") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + defer func() { _, _ = io.Copy(io.Discard, c.Request.Body) _ = c.Request.Body.Close() diff --git a/agent/git/pkg/application/application.go b/agent/git/pkg/application/application.go index 0ea3b17e..f8bd908d 100644 --- a/agent/git/pkg/application/application.go +++ b/agent/git/pkg/application/application.go @@ -50,7 +50,7 @@ func (app *Application) Routes() *gin.Engine { } router.Use(otelgin.Middleware(config.ServiceName)) - + api.RegisterHandlers(router, app) return router } diff --git a/agent/git/pkg/application/handlers.go b/agent/git/pkg/application/handlers.go index cb2c7cfe..fef778f5 100644 --- a/agent/git/pkg/application/handlers.go +++ b/agent/git/pkg/application/handlers.go @@ -20,7 +20,7 @@ func (app *Application) PostGitea(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "PostGitea") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + defer log.Println("gitea handler exited...") event := c.Request.Header.Get(string(model.GiteaHeader)) @@ -79,7 +79,7 @@ func (app *Application) PostGithub(c *gin.Context) { defer span.End() defer log.Println("github handler exited...") - + event := c.Request.Header.Get(string(model.GithubHeader)) if len(event) == 0 { log.Println("error getting the github event from header") @@ -131,7 +131,7 @@ func (app *Application) PostBitbucket(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "PostBitbucket") span.SetAttributes(attribute.String("http.method", "POST")) defer span.End() - + defer log.Println("bitbucket handler exited...") event := c.Request.Header.Get(string(model.BitBucketHeader)) diff --git a/agent/server/server.go b/agent/server/server.go index 70eb3b3e..db5f910a 100644 --- a/agent/server/server.go +++ b/agent/server/server.go @@ -41,7 +41,7 @@ func StartServer() { } r.Use(otelgin.Middleware(config.ServiceName)) - + EnableProfile(r) log.Fatal(r.Run(":8080")) } diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index b38b2315..e1cb82ad 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -35,4 +35,4 @@ dependencies: condition: grafana.enabled version: 1.0.5 repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ - + diff --git a/charts/client/templates/configmap-vertamedia-datasource.yaml b/charts/client/templates/configmap-vertamedia-datasource.yaml index 627f1dc6..e50b83e3 100644 --- a/charts/client/templates/configmap-vertamedia-datasource.yaml +++ b/charts/client/templates/configmap-vertamedia-datasource.yaml @@ -17,11 +17,11 @@ data: basicAuth: true basicAuthUser: {{ .Values.clickhouse.user }} secureJsonData: - basicAuthPassword: {{ .Values.clickhouse.password }} + basicAuthPassword: {{ .Values.clickhouse.password }} {{- else }} url: {{ .Values.existingClickhouse.host }}:8123 access: proxy - basicAuth: true + basicAuth: true {{- if not .Values.existingClickhouse.secret }} basicAuthUser: {{ .Values.existingClickhouse.username }} {{- else }} diff --git a/charts/client/values.yaml b/charts/client/values.yaml index a6caddf7..269aa52a 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -127,7 +127,7 @@ grafana: allowUiUpdates: true postgresql: enabled: false - database: + database: type: postgres host: kubviz-client-postgresql:5432 name: postgres @@ -176,7 +176,7 @@ opentelemetry: isEnabled: false url: "otelcollector.local" appName: "kubviz" - + consumer: ketallconsumer: "KETALL_EVENTS_CONSUMER" rakeesconsumer: "RAKEES_METRICS_CONSUMER" diff --git a/client/main.go b/client/main.go index af2316db..842de37f 100644 --- a/client/main.go +++ b/client/main.go @@ -24,7 +24,7 @@ func main() { log.Printf("Error shutting down tracer provider: %v", err) } }() - + app := application.Start() signals := make(chan os.Signal, 1) diff --git a/client/pkg/clients/bridge_client.go b/client/pkg/clients/bridge_client.go index 999cc07a..9b721805 100644 --- a/client/pkg/clients/bridge_client.go +++ b/client/pkg/clients/bridge_client.go @@ -46,7 +46,7 @@ func (n *NATSContext) SubscribeGitBridgeNats(conn clickhouse.DBInterface) { _, span := tracer.Start(opentelemetry.BuildContext(ctx), "SubscribeGitBridgeNats") span.SetAttributes(attribute.String("git-subscribe", "Subscribe")) defer span.End() - + n.stream.Subscribe(string(bridgeSubject), func(msg *nats.Msg) { msg.Ack() gitprovider := msg.Header.Get("GitProvider") diff --git a/client/pkg/clients/container_client.go b/client/pkg/clients/container_client.go index cea17181..ff571614 100644 --- a/client/pkg/clients/container_client.go +++ b/client/pkg/clients/container_client.go @@ -36,7 +36,7 @@ func (n *NATSContext) SubscribeContainerNats(conn clickhouse.DBInterface) { _, span := tracer.Start(opentelemetry.BuildContext(ctx), "SubscribeContainerNats") span.SetAttributes(attribute.String("container-subscribe", "Subscribe")) defer span.End() - + n.stream.Subscribe(string(containerSubject), func(msg *nats.Msg) { msg.Ack() repoName := msg.Header.Get("REPO_NAME") diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index cb89631a..00d8c796 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -14,11 +14,11 @@ Please replace the section with the specific ingress host nam Possible values are: Values | Platform | ------- | -------- | +------ | -------- | `/github` | GitHub | `/gitlab` | GitLab | `/gitea` | Gitea | -`/bitbucket` | BitBucket | +`/bitbucket` | BitBucket | `/azure` | Azure | 2. The URL for a Container Registry will appear in the following format: @@ -32,7 +32,7 @@ Please replace the section with the specific ingress host nam Possible values are: Values | Platform | ------- | -------- | +------ | -------- | `/event/docker/hub` | DockerHub | `/event/azure/container` | Azure | `/event/jfrog/container` | JFrog | diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 76918c21..20425d2e 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -28,7 +28,7 @@ You are more than welcome to open issues in this project to [suggest new feature This project is written in Golang. -You need +You need
`Go 1.16+` diff --git a/sql/0000010_trivy_misconfig.up.sql b/sql/0000010_trivy_misconfig.up.sql index 978a9ff8..11767e76 100644 --- a/sql/0000010_trivy_misconfig.up.sql +++ b/sql/0000010_trivy_misconfig.up.sql @@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS trivy_misconfig ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000011_trivyimage.up.sql b/sql/0000011_trivyimage.up.sql index 3787fa59..acab7047 100644 --- a/sql/0000011_trivyimage.up.sql +++ b/sql/0000011_trivyimage.up.sql @@ -13,6 +13,6 @@ CREATE TABLE IF NOT EXISTS trivyimage ( vul_last_modified_date DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000012_dockerhubbuild.up.sql b/sql/0000012_dockerhubbuild.up.sql index a448994e..9050b61f 100644 --- a/sql/0000012_dockerhubbuild.up.sql +++ b/sql/0000012_dockerhubbuild.up.sql @@ -8,6 +8,6 @@ CREATE TABLE IF NOT EXISTS dockerhubbuild ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000013_azurecontainerpush.up.sql b/sql/0000013_azurecontainerpush.up.sql index 6dbb5aa2..e23c03a8 100644 --- a/sql/0000013_azurecontainerpush.up.sql +++ b/sql/0000013_azurecontainerpush.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS azurecontainerpush ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; \ No newline at end of file diff --git a/sql/0000014_quaycontainerpush.up.sql b/sql/0000014_quaycontainerpush.up.sql index 6304fcc7..1361d75d 100644 --- a/sql/0000014_quaycontainerpush.up.sql +++ b/sql/0000014_quaycontainerpush.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS quaycontainerpush ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000015_trivysbom.up.sql b/sql/0000015_trivysbom.up.sql index d93aa1bb..cab4007f 100644 --- a/sql/0000015_trivysbom.up.sql +++ b/sql/0000015_trivysbom.up.sql @@ -11,6 +11,6 @@ CREATE TABLE IF NOT EXISTS trivysbom ( other_component_name String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000016_azure_devops.up.sql b/sql/0000016_azure_devops.up.sql index bc7752a8..040a25b1 100644 --- a/sql/0000016_azure_devops.up.sql +++ b/sql/0000016_azure_devops.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS azure_devops ( Event String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000017_github.up.sql b/sql/0000017_github.up.sql index 2f627dd5..b10088c6 100644 --- a/sql/0000017_github.up.sql +++ b/sql/0000017_github.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS github ( Event String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000018_gitlab.up.sql b/sql/0000018_gitlab.up.sql index d47dd988..3fde07fc 100644 --- a/sql/0000018_gitlab.up.sql +++ b/sql/0000018_gitlab.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS gitlab ( Event String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000019_bitbucket.up.sql b/sql/0000019_bitbucket.up.sql index 778e2ea7..62c2adb6 100644 --- a/sql/0000019_bitbucket.up.sql +++ b/sql/0000019_bitbucket.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS bitbucket ( Event String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000001_events.up.sql b/sql/000001_events.up.sql index d165653f..8905854f 100644 --- a/sql/000001_events.up.sql +++ b/sql/000001_events.up.sql @@ -15,6 +15,6 @@ CREATE TABLE IF NOT EXISTS events ( LastTime String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/0000020_gitea.up.sql b/sql/0000020_gitea.up.sql index 3232bc56..6767a05b 100644 --- a/sql/0000020_gitea.up.sql +++ b/sql/0000020_gitea.up.sql @@ -9,6 +9,6 @@ CREATE TABLE IF NOT EXISTS gitea ( Event String, ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000002_rakkess.up.sql b/sql/000002_rakkess.up.sql index 0bbd400a..d4b66dc2 100644 --- a/sql/000002_rakkess.up.sql +++ b/sql/000002_rakkess.up.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS rakkess ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000003_DeprecatedAPIs.up.sql b/sql/000003_DeprecatedAPIs.up.sql index 46be7b93..c8aa1f76 100644 --- a/sql/000003_DeprecatedAPIs.up.sql +++ b/sql/000003_DeprecatedAPIs.up.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS DeprecatedAPIs ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000004_DeletedAPIs.up.sql b/sql/000004_DeletedAPIs.up.sql index d333b3a4..dbfe3360 100644 --- a/sql/000004_DeletedAPIs.up.sql +++ b/sql/000004_DeletedAPIs.up.sql @@ -10,6 +10,6 @@ CREATE TABLE IF NOT EXISTS DeletedAPIs ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000005_jfrogcontainerpush.up.sql b/sql/000005_jfrogcontainerpush.up.sql index a3a98fa6..4f47f001 100644 --- a/sql/000005_jfrogcontainerpush.up.sql +++ b/sql/000005_jfrogcontainerpush.up.sql @@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS jfrogcontainerpush ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000006_getall_resources.up.sql b/sql/000006_getall_resources.up.sql index 32168213..b77b322c 100644 --- a/sql/000006_getall_resources.up.sql +++ b/sql/000006_getall_resources.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS getall_resources ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000007_outdated_images.up.sql b/sql/000007_outdated_images.up.sql index 9779aead..f89c6132 100644 --- a/sql/000007_outdated_images.up.sql +++ b/sql/000007_outdated_images.up.sql @@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS outdated_images ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000008_kubescore.up.sql b/sql/000008_kubescore.up.sql index 2db441b1..db4086f1 100644 --- a/sql/000008_kubescore.up.sql +++ b/sql/000008_kubescore.up.sql @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS kubescore ( EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; diff --git a/sql/000009_trivy_vul.up.sql b/sql/000009_trivy_vul.up.sql index 2ebc670f..808a909f 100644 --- a/sql/000009_trivy_vul.up.sql +++ b/sql/000009_trivy_vul.up.sql @@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS trivy_vul ( vul_last_modified_date DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL -) ENGINE = MergeTree() -ORDER BY ExpiryDate +) ENGINE = MergeTree() +ORDER BY ExpiryDate TTL ExpiryDate; From a8d2e7d58dd42efd740d9cace705da7d3b76b456 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Thu, 4 Apr 2024 13:52:32 +0530 Subject: [PATCH 248/263] Feat: Add kuberhealthy as dependency --- charts/agent/Chart.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 296eb7b5..97a956b5 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,10 +15,15 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.16 +version: 1.1.17 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "v1.1.5" +dependencies: + - name: kuberhealthy + condition: kuberhealthy.enabled + version: 1.x.x + repository: https://kube-tarian.github.io/helmrepo-supporting-tools/ From 748b6d66117f5125bf6cd846e76149eb5dc63efd Mon Sep 17 00:00:00 2001 From: alanjino Date: Thu, 4 Apr 2024 20:36:20 +0530 Subject: [PATCH 249/263] pre-commit implementation Signed-off-by: alanjino --- .pre-commit-config.yaml | 42 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 18725306..5f5fcbfd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -68,9 +68,6 @@ repos: - id: check-devcontainer name: Check devcontainer description: Checks for existance of .devcontainer.json in the project - - id: check-dockerfile - name: Check Dockerfile - description: Enforce use of Chainguard base images in Dockefiles - id: check-gitsign name: Check gitsign description: Check if the last commit is signed with Sigstore gitsign @@ -85,3 +82,42 @@ repos: name: Verify YAML syntax args: - --allow-multiple-documents +- repo: https://github.com/hadolint/hadolint + rev: v2.12.0 + hooks: + - id: hadolint + # Rules you want to ignore may be found here: https://github.com/hadolint/hadolint?tab=readme-ov-file#rules + name: Dockerfile linter + description: Dockerfile linter following best-practices + args: [--ignore, DL3051] + +- repo: local + hooks: + - name: Check Dockerfile + id: check-dockerfile-sh + entry: bash + args: + - -c + - | + check_dockerfile() { + if [[ $1 == *"Dockerfile"* ]]; then + base_image=$(grep '^FROM' "$1" | awk '{print $2}') + if [[ $base_image != golang:* ]]; then + echo "Error: Base image in $1 is not from cgr.dev/chianguard" + return 1 + fi + fi + return 0 + } + + export -f check_dockerfile + + if find . -type f -exec bash -c 'check_dockerfile "$0"' {} \; | grep -q 'Error'; then + echo "Commit failed due to non-compliant Dockerfile(s)." + exit 1 + fi + + echo "All Dockerfiles are compliant." + exit 0 + language: system + pass_filenames: false \ No newline at end of file From 80a54a27533ff562ec9c7fd4997e2d93239ee444 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 5 Apr 2024 12:29:30 +0530 Subject: [PATCH 250/263] kuberhealthy-dashboard --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 2 +- charts/client/Chart.yaml | 2 +- .../configmap-kuberhealthy-dashboard.yaml | 1216 +++++++++++++++++ grafana/kuberhealthy-dashboard.json | 1204 ++++++++++++++++ 5 files changed, 2423 insertions(+), 3 deletions(-) create mode 100644 charts/client/templates/configmap-kuberhealthy-dashboard.yaml create mode 100644 grafana/kuberhealthy-dashboard.json diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 97a956b5..107290fe 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.17 +version: 1.1.18 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index e06c9280..5b614e44 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -191,7 +191,7 @@ schedule: kuberhealthy: enabled: true pollInterval: "60m" - url: "http://localhost:8080" + url: "http://kuberhealthy:8080" opentelemetry: isEnabled: false diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index b38b2315..f24aef1c 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.21 +version: 1.1.22 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-kuberhealthy-dashboard.yaml b/charts/client/templates/configmap-kuberhealthy-dashboard.yaml new file mode 100644 index 00000000..6221c6ac --- /dev/null +++ b/charts/client/templates/configmap-kuberhealthy-dashboard.yaml @@ -0,0 +1,1216 @@ +{{- if .Values.dashboards.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "client.fullname" . }}-kuberhealthy-dashboard + annotations: + grafana_folder: "Kubviz" + labels: + {{ .Values.dashboards.label }}: {{ .Values.dashboards.labelValue | quote }} +data: + kuberhealthy.json: |- + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 0 + }, + "id": 14, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "editorMode": "code", + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + }, + "visualEditor": { + "code": "console.log(context);\nreturn {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "codeHeight": 600, + "dataset": [], + "series": [] + } + }, + "pluginVersion": "5.3.0", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "editorMode": "builder", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '1'\nGROUP BY CheckName", + "rawQuery": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '1'\nGROUP BY CheckName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Running kuberhealthy checks", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "gridPos": { + "h": 12, + "w": 18, + "x": 0, + "y": 13 + }, + "id": 15, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "editorMode": "code", + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + }, + "visualEditor": { + "code": "console.log(context);\nreturn {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "codeHeight": 600, + "dataset": [], + "series": [] + } + }, + "pluginVersion": "5.3.0", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "{{ .Values.datasources.uid }}" + }, + "dateTimeType": "DATETIME", + "editorMode": "builder", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '0'\nGROUP BY CheckName", + "rawQuery": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '0'\nGROUP BY CheckName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": " Faliure kuberhealthy checks", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere CheckName LIKE '%pod-restarts%' AND $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Kuberhealthy Pod Restarts Status Over Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "OK" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK\nfrom default.kuberhealthy\nwhere $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Kuberhealthy Status Over Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "count()" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 33 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, count(*)\nfrom default.kuberhealthy\nwhere $__timeFilter(LastRun)\nGroup By LastRun", + "refId": "A" + } + ], + "title": "Kuberhealthy Counts Over Time.", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 33 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere CheckName LIKE '%image-pull-check%' AND $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Image Pull Check", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 5, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere CheckName LIKE '%resource-quota%' AND $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Resource-quata check", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 41 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere $__timeFilter(LastRun) AND OK='0'\nOrder BY LastRun DESC", + "refId": "A" + } + ], + "title": "Kuberhealthy Errors Over Time", + "type": "table" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 49 + }, + "id": 3, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT * FROM \"default\".\"kuberhealthy\"\nWhere $__timeFilter(LastRun)\nOrder By LastRun DESC \n\n", + "refId": "A" + } + ], + "title": "Kuberhealthy Tables", + "type": "table" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 57 + }, + "id": 16, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '1'\nGROUP BY CheckName", + "refId": "A" + } + ], + "title": "Success Kuberhealthy Checks", + "type": "bargauge" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 11, + "x": 12, + "y": 57 + }, + "id": 2, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '0'\nGROUP BY CheckName", + "refId": "A" + } + ], + "title": "Faliure Kuberhealthy Checks", + "type": "bargauge" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 65 + }, + "id": 4, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT count(OK)\nFROM default.kuberhealthy\nWHERE OK = '1'", + "refId": "A" + } + ], + "title": "Total count Kuberhealthy Running", + "type": "gauge" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 65 + }, + "id": 5, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT count(OK)\nFROM default.kuberhealthy\nWHERE OK = '0'", + "refId": "A" + } + ], + "title": "Total Count Kuberhealthy Faliures", + "type": "gauge" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "KuberHealth", + "uid": "d946c53c-8b1d-4e3c-9154-4219165342", + "version": 2, + "weekStart": "" + } +{{- end }} \ No newline at end of file diff --git a/grafana/kuberhealthy-dashboard.json b/grafana/kuberhealthy-dashboard.json new file mode 100644 index 00000000..915b7747 --- /dev/null +++ b/grafana/kuberhealthy-dashboard.json @@ -0,0 +1,1204 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 0 + }, + "id": 14, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "editorMode": "code", + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + }, + "visualEditor": { + "code": "console.log(context);\nreturn {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "codeHeight": 600, + "dataset": [], + "series": [] + } + }, + "pluginVersion": "5.3.0", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "editorMode": "builder", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '1'\nGROUP BY CheckName", + "rawQuery": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '1'\nGROUP BY CheckName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": "Running kuberhealthy checks", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "gridPos": { + "h": 12, + "w": 18, + "x": 0, + "y": 13 + }, + "id": 15, + "options": { + "baidu": { + "callback": "bmapReady", + "key": "" + }, + "editor": { + "format": "auto", + "height": 600 + }, + "editorMode": "code", + "gaode": { + "key": "", + "plugin": "AMap.Scale,AMap.ToolBar" + }, + "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "google": { + "callback": "gmapReady", + "key": "" + }, + "map": "none", + "renderer": "canvas", + "themeEditor": { + "config": "{}", + "height": 400, + "name": "default" + }, + "visualEditor": { + "code": "console.log(context);\nreturn {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "codeHeight": 600, + "dataset": [], + "series": [] + } + }, + "pluginVersion": "5.3.0", + "targets": [ + { + "datasource": { + "type": "vertamedia-clickhouse-datasource", + "uid": "vertamedia-clickhouse-datasource" + }, + "dateTimeType": "DATETIME", + "editorMode": "builder", + "extrapolate": true, + "format": "table", + "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", + "intervalFactor": 1, + "query": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '0'\nGROUP BY CheckName", + "rawQuery": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '0'\nGROUP BY CheckName", + "refId": "A", + "round": "0s", + "skip_comments": true + } + ], + "title": " Faliure kuberhealthy checks", + "type": "volkovlabs-echarts-panel" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere CheckName LIKE '%pod-restarts%' AND $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Kuberhealthy Pod Restarts Status Over Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 2, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "OK" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK\nfrom default.kuberhealthy\nwhere $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Kuberhealthy Status Over Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "count()" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 33 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, count(*)\nfrom default.kuberhealthy\nwhere $__timeFilter(LastRun)\nGroup By LastRun", + "refId": "A" + } + ], + "title": "Kuberhealthy Counts Over Time.", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 3, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 33 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere CheckName LIKE '%image-pull-check%' AND $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Image Pull Check", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 5, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere CheckName LIKE '%resource-quota%' AND $__timeFilter(LastRun)", + "refId": "A" + } + ], + "title": "Resource-quata check", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 41 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "Select LastRun, OK,Errors\nfrom default.kuberhealthy\nwhere $__timeFilter(LastRun) AND OK='0'\nOrder BY LastRun DESC", + "refId": "A" + } + ], + "title": "Kuberhealthy Errors Over Time", + "type": "table" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "center", + "cellOptions": { + "type": "color-text" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 49 + }, + "id": 3, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT * FROM \"default\".\"kuberhealthy\"\nWhere $__timeFilter(LastRun)\nOrder By LastRun DESC \n\n", + "refId": "A" + } + ], + "title": "Kuberhealthy Tables", + "type": "table" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 57 + }, + "id": 16, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '1'\nGROUP BY CheckName", + "refId": "A" + } + ], + "title": "Success Kuberhealthy Checks", + "type": "bargauge" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 11, + "x": 12, + "y": 57 + }, + "id": 2, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT CheckName, count(OK) AS counts\nFROM default.kuberhealthy\nWHERE OK = '0'\nGROUP BY CheckName", + "refId": "A" + } + ], + "title": "Faliure Kuberhealthy Checks", + "type": "bargauge" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 65 + }, + "id": 4, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT count(OK)\nFROM default.kuberhealthy\nWHERE OK = '1'", + "refId": "A" + } + ], + "title": "Total count Kuberhealthy Running", + "type": "gauge" + }, + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 65 + }, + "id": 5, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "10.0.3", + "targets": [ + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": "ClickHouse" + }, + "editorType": "sql", + "format": 1, + "meta": { + "builderOptions": { + "columns": [], + "database": "", + "limit": 1000, + "mode": "list", + "queryType": "table", + "table": "" + } + }, + "pluginVersion": "4.0.3", + "queryType": "table", + "rawSql": "SELECT count(OK)\nFROM default.kuberhealthy\nWHERE OK = '0'", + "refId": "A" + } + ], + "title": "Total Count Kuberhealthy Faliures", + "type": "gauge" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "KuberHealth", + "uid": "d946c53c-8b1d-4e3c-9154-4219165342", + "version": 2, + "weekStart": "" +} From c328abf4619064e8b0c05e86c9d599467a6bb0f4 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Fri, 5 Apr 2024 16:03:13 +0530 Subject: [PATCH 251/263] config: Update image tag to v1.1.6 --- charts/agent/Chart.yaml | 4 +- charts/agent/values.yaml | 81 ++++++++++++++++++++++++++++++++++++--- charts/client/Chart.yaml | 4 +- charts/client/values.yaml | 4 +- 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 107290fe..5e80c02f 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.18 +version: 1.1.19 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.5" +appVersion: "v1.1.6" dependencies: - name: kuberhealthy condition: kuberhealthy.enabled diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 5b614e44..46b6980d 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/kubviz-agent pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.5" + tag: "v1.1.6" imagePullSecrets: [] nameOverride: "" @@ -45,13 +45,12 @@ service: port: 80 - git_bridge: enabled: false image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always - tag: "v1.1.5" + tag: "v1.1.6" resources: limits: cpu: 200m @@ -88,14 +87,12 @@ git_bridge: # - chart-example.local - - container_bridge: enabled: false image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always - tag: "v1.1.5" + tag: "v1.1.6" resources: limits: cpu: 200m @@ -192,6 +189,78 @@ kuberhealthy: enabled: true pollInterval: "60m" url: "http://kuberhealthy:8080" + check: + podRestarts: + enabled: true + runInterval: 5m + timeout: 10m + image: + registry: docker.io + repository: kuberhealthy/pod-restarts-check + tag: v2.5.0 + allNamespaces: true + extraEnvs: + MAX_FAILURES_ALLOWED: "10" + nodeSelector: {} + tolerations: [] + #- key: "key" + # operator: "Equal" + # value: "value" + # effect: "NoSchedule" + resources: + requests: + cpu: 10m + memory: 50Mi + podStatus: + enabled: true + runInterval: 5m + timeout: 15m + image: + registry: docker.io + repository: kuberhealthy/pod-status-check + tag: v1.3.0 + allNamespaces: true + extraEnvs: {} + nodeSelector: {} + tolerations: [] + #- key: "key" + # operator: "Equal" + # value: "value" + # effect: "NoSchedule" + resources: + requests: + cpu: 10m + memory: 50Mi + imagePullCheck: + enabled: true + runInterval: 60m + timeout: 1m + image: + repository: kuberhealthy/test-check + tag: v1.4.0 + extraEnvs: + REPORT_FAILURE: "false" + REPORT_DELAY: "1s" + resources: + requests: + cpu: 10m + memory: 50Mi + resourceQuota: + enabled: true + runInterval: 1h + timeout: 2m + image: + repository: kuberhealthy/resource-quota-check + tag: v1.3.0 + extraEnvs: + BLACKLIST: "default" + WHITELIST: "kube-system,kubviz" + resources: + requests: + cpu: 15m + memory: 15Mi + limits: + cpu: 30m opentelemetry: isEnabled: false diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index f24aef1c..76896324 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.22 +version: 1.1.23 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.5" +appVersion: "v1.1.6" dependencies: - name: nats condition: nats.enabled diff --git a/charts/client/values.yaml b/charts/client/values.yaml index a6caddf7..201cd1ee 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/client pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.5" + tag: "v1.1.6" imagePullSecrets: [] nameOverride: "" @@ -164,7 +164,7 @@ migration: image: repository: ghcr.io/intelops/kubviz/migration pullPolicy: Always - tag: "v1.1.5" + tag: "v1.1.6" schema: path: "/sql" From 9b46c48c3e718ef01377e2acd51ece2a98b87bf2 Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 5 Apr 2024 18:15:28 +0530 Subject: [PATCH 252/263] kuberhealthy-trivy-enhance-readme --- README.md | 31 ++++++++++++ docs/CONFIGURATION_HEALTHCHECK.md | 82 +++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 docs/CONFIGURATION_HEALTHCHECK.md diff --git a/README.md b/README.md index f9ce3923..9a0d2cc4 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,37 @@ The TTL value is customizable, empowering you to define the specific duration af To guide you through the process of setting up a TTL, [please follow these steps](docs/CONFIGURATION_TTL.md) +#### Customizing Security Scanning + +KubViz enables you to perform cluster scans, image scans, and SBOM creation in CycloneDX format. Utilizing this scan, vulnerabilities can be identified. + +You can customize the security scans by changing the chart values. + +- To [Disable](https://github.com/intelops/kubviz/blob/main/charts/agent/values.yaml#L186) the cluster scan you can pass 0 or empty string + +```yaml +schedule: + enabled: true + trivyclusterscanInterval: 0 +... +``` +- For changing the interval, pass the interval time + +```yaml +schedule: + enabled: true + trivyclusterscanInterval: "@every 24h" +... +``` + +Same you can change for [image-scan](https://github.com/intelops/kubviz/blob/main/charts/agent/values.yaml#L184) and [sbom](https://github.com/intelops/kubviz/blob/main/charts/agent/values.yaml#L185) + +## Health Check + +You can run different types of checks against your Kubernetes cluster to detect any issues or potential problems before they cause any downtime or service disruptions. Check will run in the background and sends data to kubviz. After analysing the data from dashboard you can take corrective action quickly, if any issues are detected. + +Please check the [configuration](docs/CONFIGURATION_HEALTHCHECK.md) for health checks + ## Use Cases ### Cluster Event Tracking diff --git a/docs/CONFIGURATION_HEALTHCHECK.md b/docs/CONFIGURATION_HEALTHCHECK.md new file mode 100644 index 00000000..4c056750 --- /dev/null +++ b/docs/CONFIGURATION_HEALTHCHECK.md @@ -0,0 +1,82 @@ +## Introduction + +All health checks are enabled by default upon installing the KubViz agent. They are automatically included, but if you don't need them, you can disable them. + +```yaml +kuberhealthy: + enabled: false +... +``` + +## Types of Checks + +Check Name | Description | +------ | -------- | +Daemonset check | Ensures daemonsets can be successfully deployed | +DNS status check | Checks for failures with DNS, including resolving within the cluster and outside of the cluster | +Deployment check | Ensures that a Deployment and Service can be provisioned, created, and serve traffic within the Kubernetes cluster | +Image pull check | Verifies that an image can be pulled from an image repository | +Pod status check | Checks for unhealthy pod statuses in a target namespace | +Pod restart | Checks for excessive pod restarts in any namespace | +Resource quota check | Checks if resource quotas (CPU & memory) are available | + +## Configuration + +- Daemonset, Deployment, and DNS checks are enabled by default. + +- Pod Status, Pod Restart, Image Pull, and Resource Quota checks need to be manually enabled. + +```yaml +check: + podRestarts: + enabled: true +... +``` + +```yaml + podStatus: + enabled: true +... +``` + +```yaml + imagePullCheck: + enabled: true +... +``` + +```yaml + resourceQuota: + enabled: true +... +``` + +### Additional configuration for image-pull check + +1. Pull the test image from docker hub + +```bash +docker pull kuberhealthy/test-check +``` + +2. Push this image on the repository you need tested. + +```bash +docker push my.repository/repo/test-check +``` + +- The pod is designed to attempt a pull of the test image from the remote repository (never from local). If the image is unavailable, an error will be reported to the API + +### Additional configuration for resource quota check + +This check tests if namespace resource quotas CPU and memory are under a specified threshold or percentage. + +You need to add the namespaces to the 'WHITELIST'. + +```yaml + extraEnvs: + BLACKLIST: "default" + WHITELIST: "kube-system,kubviz" +... +``` + From 711d73e1b01d0bc89cf058884b9035db2bf810a8 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Sat, 6 Apr 2024 08:44:51 +0530 Subject: [PATCH 253/263] apache echart fix --- charts/client/Chart.yaml | 2 +- .../templates/configmap-azure-dashboard.yaml | 53 ++++++++++--- .../configmap-bitbucket-dashboard.yaml | 42 ++++++++--- .../templates/configmap-gitea-dashboard.yaml | 55 ++++++++++---- .../templates/configmap-github-dashboard.yaml | 62 ++++++++++++--- .../templates/configmap-gitlab-dashboard.yaml | 41 ++++++++-- .../configmap-kubedata-dashboard.yaml | 71 +++++++++++++++--- .../configmap-kuberhealthy-dashboard.yaml | 7 +- .../templates/configmap-kubviz-dashboard.yaml | 44 ++++++----- .../templates/configmap-trivy-dashboard.yaml | 74 +++++++++--------- grafana/azure-dashboard.json | 54 ++++++++++--- grafana/bitBucket-dashboard.json | 43 ++++++++--- grafana/giTea-dashboard.json | 56 ++++++++++---- grafana/gitHub-dashboard.json | 63 +++++++++++++--- grafana/gitLab-dashboard.json | 42 ++++++++--- grafana/kubeData-dashboard.json | 70 ++++++++++++++--- grafana/kuberhealthy-dashboard.json | 6 +- grafana/kubvizDsahboard.json | 45 ++++++----- grafana/trivy-dashboard.json | 75 ++++++++++--------- 19 files changed, 659 insertions(+), 246 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 76896324..9a650535 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.23 +version: 1.1.24 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-azure-dashboard.yaml b/charts/client/templates/configmap-azure-dashboard.yaml index ea290c78..98739e6a 100644 --- a/charts/client/templates/configmap-azure-dashboard.yaml +++ b/charts/client/templates/configmap-azure-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 33, + "id": 53, "links": [], "liveNow": false, "panels": [ @@ -51,11 +51,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -66,8 +67,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -110,11 +117,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -125,8 +133,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -169,11 +183,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -184,8 +199,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -223,7 +244,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -293,7 +315,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -370,7 +393,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -426,7 +450,11 @@ data: "templating": { "list": [ { - "current": {}, + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" @@ -445,7 +473,11 @@ data: "type": "query" }, { - "current": {}, + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "{{ .Values.datasources.uid }}" @@ -473,7 +505,8 @@ data: "timezone": "", "title": "Azure", "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", - "version": 1, + "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-bitbucket-dashboard.yaml b/charts/client/templates/configmap-bitbucket-dashboard.yaml index fde60b9f..3b2722db 100644 --- a/charts/client/templates/configmap-bitbucket-dashboard.yaml +++ b/charts/client/templates/configmap-bitbucket-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 34, + "id": 60, "links": [], "liveNow": false, "panels": [ @@ -51,11 +51,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -66,8 +67,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -110,11 +117,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -125,8 +133,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -169,11 +183,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -184,8 +199,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -223,7 +244,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -293,7 +315,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -451,8 +474,8 @@ data: { "current": { "selected": false, - "text": "", - "value": "" + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -481,7 +504,8 @@ data: "timezone": "", "title": "BitBucket", "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", - "version": 1, + "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-gitea-dashboard.yaml b/charts/client/templates/configmap-gitea-dashboard.yaml index 3c1a663f..5d53ed2b 100644 --- a/charts/client/templates/configmap-gitea-dashboard.yaml +++ b/charts/client/templates/configmap-gitea-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 35, + "id": 61, "links": [], "liveNow": false, "panels": [ @@ -51,11 +51,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -66,8 +67,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -110,11 +117,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -125,8 +133,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -169,11 +183,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -184,8 +199,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -223,7 +244,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -293,7 +315,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -370,7 +393,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -427,9 +451,13 @@ data: "list": [ { "current": { - "selected": false, - "text": "All", - "value": "$__all" + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -451,8 +479,8 @@ data: { "current": { "selected": false, - "text": "", - "value": "" + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -481,7 +509,8 @@ data: "timezone": "", "title": "GiTea", "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", - "version": 1, + "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-github-dashboard.yaml b/charts/client/templates/configmap-github-dashboard.yaml index 860f77a1..38a98a43 100644 --- a/charts/client/templates/configmap-github-dashboard.yaml +++ b/charts/client/templates/configmap-github-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 37, + "id": 57, "links": [], "liveNow": false, "panels": [ @@ -51,11 +51,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", "google": { "callback": "gmapReady", "key": "" @@ -66,9 +67,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, - "pluginVersion": "10.0.3", + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -111,11 +117,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -126,8 +133,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -170,11 +183,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -185,8 +199,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -229,11 +249,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "getOption": "const values = context.panel.data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", "google": { "callback": "gmapReady", "key": "" @@ -244,8 +265,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -288,11 +315,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "getOption": "const values = context.panel.data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", "google": { "callback": "gmapReady", "key": "" @@ -303,8 +331,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -349,7 +383,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -406,9 +441,13 @@ data: "list": [ { "current": { - "selected": false, - "text": "", - "value": "" + "selected": true, + "text": [ + "ahinvinith" + ], + "value": [ + "ahinvinith" + ] }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -467,4 +506,5 @@ data: "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-gitlab-dashboard.yaml b/charts/client/templates/configmap-gitlab-dashboard.yaml index bc1273b7..fd1840dc 100644 --- a/charts/client/templates/configmap-gitlab-dashboard.yaml +++ b/charts/client/templates/configmap-gitlab-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 36, + "id": 59, "links": [], "liveNow": false, "panels": [ @@ -51,11 +51,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -66,8 +67,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -110,11 +117,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -125,8 +133,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -169,11 +183,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -184,8 +199,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -223,7 +244,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -293,7 +315,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -370,7 +393,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -485,7 +509,8 @@ data: "timezone": "", "title": "GitLab", "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", - "version": 1, + "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubedata-dashboard.yaml b/charts/client/templates/configmap-kubedata-dashboard.yaml index 7e275444..d479fd9b 100644 --- a/charts/client/templates/configmap-kubedata-dashboard.yaml +++ b/charts/client/templates/configmap-kubedata-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 146, + "id": 56, "links": [], "liveNow": false, "panels": [ @@ -57,11 +57,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values;\n const namespaces = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const pods = data.series[0].fields[3].values;\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const reason = reasons[i];\n const pod = pods[i];\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const reasonNode = { name: reason, children: [{ name: `Count: ${pod}` }] };\n namespaceNode.children.push(reasonNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const namespaces = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const pods = context.panel.data.series[0].fields[3].values;\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const reason = reasons[i];\n const pod = pods[i];\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const reasonNode = { name: reason, children: [{ name: `Count: ${pod}` }] };\n namespaceNode.children.push(reasonNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;", "google": { "callback": "gmapReady", "key": "" @@ -72,8 +73,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -116,11 +123,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const reasons = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Check if reasons and counts are defined and not empty\n if (!reasons || !counts || reasons.length === 0 || counts.length === 0) {\n // Display a message when no data is available\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n } else {\n // Data is available, proceed with the chart creation\n // Create an array of data items, each containing name and value\n const seriesData = reasons.map((reason, index) => ({\n name: reason,\n value: counts[index],\n }));\n\n // Define a custom color for the bars\n const customColor = 'rgb(0, 123, 255)'; // Change this to your desired color\n\n // Apache ECharts option\n option = {\n xAxis: {\n type: 'category',\n data: reasons, // Use the reasons directly for xAxis data\n axisLabel: {\n interval: 0, // Display all labels on the xAxis\n },\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Pods'], // Legend name\n left: 'left', // Position the legend on the left side\n bottom: 'bottom', // Position the legend at the bottom\n },\n series: [\n {\n name: 'Pods', // Series name for the legend\n data: seriesData,\n type: 'bar',\n label: {\n show: true,\n position: 'top',\n formatter: '{c}',\n },\n itemStyle: {\n barBorderRadius: [5, 5, 0, 0], // Adjust the values to control the curvature\n color: customColor, // Set the custom color for the bars\n },\n },\n ],\n };\n }\n}\n\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const reasons = context.panel.data.series[0].fields[0].values;\n const counts = context.panel.data.series[0].fields[1].values;\n\n // Check if reasons and counts are defined and not empty\n if (!reasons || !counts || reasons.length === 0 || counts.length === 0) {\n // Display a message when no data is available\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n } else {\n // Data is available, proceed with the chart creation\n // Create an array of data items, each containing name and value\n const seriesData = reasons.map((reason, index) => ({\n name: reason,\n value: counts[index],\n }));\n\n // Define a custom color for the bars\n const customColor = 'rgb(0, 123, 255)'; // Change this to your desired color\n\n // Apache ECharts option\n option = {\n xAxis: {\n type: 'category',\n data: reasons, // Use the reasons directly for xAxis data\n axisLabel: {\n interval: 0, // Display all labels on the xAxis\n },\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Pods'], // Legend name\n left: 'left', // Position the legend on the left side\n bottom: 'bottom', // Position the legend at the bottom\n },\n series: [\n {\n name: 'Pods', // Series name for the legend\n data: seriesData,\n type: 'bar',\n label: {\n show: true,\n position: 'top',\n formatter: '{c}',\n },\n itemStyle: {\n barBorderRadius: [5, 5, 0, 0], // Adjust the values to control the curvature\n color: customColor, // Set the custom color for the bars\n },\n },\n ],\n };\n }\n}\n\nreturn option;", "google": { "callback": "gmapReady", "key": "" @@ -131,8 +139,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -175,11 +189,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const counts = context.panel.data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", "google": { "callback": "gmapReady", "key": "" @@ -190,8 +205,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -234,11 +255,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const counts = context.panel.data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", "google": { "callback": "gmapReady", "key": "" @@ -249,8 +271,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -293,11 +321,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);\n\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const counts = context.panel.data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);\n\n", "google": { "callback": "gmapReady", "key": "" @@ -308,8 +337,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -352,11 +387,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Root', // You can customize the name of the root node if needed\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly as root nodes\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON\n const clusters = context.panel.data.series[0].fields[0].values;\n const hosts = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const eventTimes = context.panel.data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Root', // You can customize the name of the root node if needed\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly as root nodes\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -367,8 +403,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -411,11 +453,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values; // New column for ClusterName\n const namespaces = data.series[0].fields[1].values;\n const kinds = data.series[0].fields[2].values; // Adjusted index for Kind\n const counts = data.series[0].fields[3].values; // New column for Count\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = context.panel.data.series[0].fields[0].values; // New column for ClusterName\n const namespaces = context.panel.data.series[0].fields[1].values;\n const kinds = context.panel.data.series[0].fields[2].values; // Adjusted index for Kind\n const counts = context.panel.data.series[0].fields[3].values; // New column for Count\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -426,8 +469,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -466,8 +515,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -706,8 +754,9 @@ data: "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 2, + "version": 3, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kuberhealthy-dashboard.yaml b/charts/client/templates/configmap-kuberhealthy-dashboard.yaml index 6221c6ac..70e94c26 100644 --- a/charts/client/templates/configmap-kuberhealthy-dashboard.yaml +++ b/charts/client/templates/configmap-kuberhealthy-dashboard.yaml @@ -56,7 +56,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = context.panel.data.series[0].fields[0].values;\n const counts = context.panel.data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -124,7 +124,7 @@ data: "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = context.panel.data.series[0].fields[0].values;\n const counts = context.panel.data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -1210,7 +1210,8 @@ data: "timezone": "", "title": "KuberHealth", "uid": "d946c53c-8b1d-4e3c-9154-4219165342", - "version": 2, + "version": 3, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-kubviz-dashboard.yaml b/charts/client/templates/configmap-kubviz-dashboard.yaml index 6ee2c56c..206fc3d8 100644 --- a/charts/client/templates/configmap-kubviz-dashboard.yaml +++ b/charts/client/templates/configmap-kubviz-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 32, + "id": 64, "links": [], "liveNow": false, "panels": [ @@ -205,11 +205,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const reasons = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const eventTimes = data.series[0].fields[2].values;\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n reasons.forEach((reason, index) => {\n const sourceNode = {\n name: reason,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const kindNode = {\n name: kinds[index],\n category: 1, // Category for kind nodes\n symbolSize: 40, // Size for kind nodes\n };\n\n const eventTimeNode = {\n name: eventTimes[index],\n category: 2, // Category for eventTime nodes\n symbolSize: 20, // Size for eventTime nodes\n };\n\n // Ensure source, kind, and eventTime nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === kindNode.name)) {\n nodes.push(kindNode);\n }\n\n if (!nodes.some((node) => node.name === eventTimeNode.name)) {\n nodes.push(eventTimeNode);\n }\n\n // Create links between reason, kind, and eventTime nodes\n links.push({\n source: reason,\n target: kinds[index],\n });\n\n links.push({\n source: kinds[index],\n target: eventTimes[index],\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Reasons',\n },\n {\n name: 'Nodes',\n },\n {\n name: 'Event Times',\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Reasons', 'Nodes', 'Event Times'],\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: {\n color: '#000',\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n edgeSymbolSize: [12, 12],\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "getOption": "// Check if context.panel.data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const reasons = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const eventTimes = context.panel.data.series[0].fields[2].values;\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n reasons.forEach((reason, index) => {\n const sourceNode = {\n name: reason,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const kindNode = {\n name: kinds[index],\n category: 1, // Category for kind nodes\n symbolSize: 40, // Size for kind nodes\n };\n\n const eventTimeNode = {\n name: eventTimes[index],\n category: 2, // Category for eventTime nodes\n symbolSize: 20, // Size for eventTime nodes\n };\n\n // Ensure source, kind, and eventTime nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === kindNode.name)) {\n nodes.push(kindNode);\n }\n\n if (!nodes.some((node) => node.name === eventTimeNode.name)) {\n nodes.push(eventTimeNode);\n }\n\n // Create links between reason, kind, and eventTime nodes\n links.push({\n source: reason,\n target: kinds[index],\n });\n\n links.push({\n source: kinds[index],\n target: eventTimes[index],\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Reasons',\n },\n {\n name: 'Nodes',\n },\n {\n name: 'Event Times',\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Reasons', 'Nodes', 'Event Times'],\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: {\n color: '#000',\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n edgeSymbolSize: [12, 12],\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display \"Data not available\" in the panel\n return {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n}\n", "google": { "callback": "gmapReady", "key": "" @@ -220,8 +221,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -264,11 +271,12 @@ data: "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let kind = [];\nlet resources = [];\n\ndata.series.map((s) => {\n kind = s.fields.find((f) => f.name === 'Kind').values;\n resources = s.fields.find((f) => f.name === 'Resources').values;\n});\n\n// Create an empty array to store doughnut chart data\nconst doughnutChartData = [];\n\n// Define colors for doughnut slices\nconst doughnutSliceColors = ['#235894', '#FFFF00', '#FF0000', '#00FF00', '#FFA500'];\n\n// Map severity and counts to doughnut chart data\nkind.forEach((kinddata, index) => {\n doughnutChartData.push({\n value: resources[index],\n name: kinddata,\n itemStyle: {\n borderRadius: [10, 10, 10, 10], // Add rounded corners\n color: doughnutSliceColors[index % doughnutSliceColors.length],\n borderWidth: 2,\n borderColor: '#fff',\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {\n trigger: 'item',\n },\n legend: {\n top: '5%',\n left: 'center',\n },\n series: [\n {\n name: 'Access From',\n type: 'pie',\n radius: ['40%', '70%'],\n avoidLabelOverlap: false,\n label: {\n show: false,\n position: 'center',\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 40,\n fontWeight: 'bold',\n },\n },\n labelLine: {\n show: false,\n },\n data: doughnutChartData, // Use the modified doughnut chart data\n },\n ],\n};", + "getOption": "let kind = [];\nlet resources = [];\n\ncontext.panel.data.series.forEach((s) => {\n const kindField = s.fields.find((f) => f.name === 'Kind');\n const resourcesField = s.fields.find((f) => f.name === 'Resources');\n if (kindField && resourcesField) {\n kind = kindField.values;\n resources = resourcesField.values;\n }\n});\n\n// Create an empty array to store doughnut chart data\nconst doughnutChartData = [];\n\n// Define colors for doughnut slices\nconst doughnutSliceColors = ['#235894', '#FFFF00', '#FF0000', '#00FF00', '#FFA500'];\n\n// Map kind and resources counts to doughnut chart data\nkind.forEach((kinddata, index) => {\n doughnutChartData.push({\n value: resources[index],\n name: kinddata,\n clusterName: context.panel.data.series[0].fields[0].values[index], // Extract cluster name\n itemStyle: {\n borderRadius: [10, 10, 10, 10], // Add rounded corners\n color: doughnutSliceColors[index % doughnutSliceColors.length],\n borderWidth: 2,\n borderColor: '#fff',\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {\n trigger: 'item',\n formatter: function (params) {\n return `Resource From
${params.data.clusterName} ${params.value}`;\n },\n },\n legend: {\n top: '5%',\n left: 'center',\n },\n series: [\n {\n name: '',\n type: 'pie',\n radius: ['40%', '70%'],\n avoidLabelOverlap: false,\n label: {\n show: false,\n position: 'center',\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 40,\n fontWeight: 'bold',\n },\n },\n labelLine: {\n show: false,\n },\n data: doughnutChartData, // Use the modified doughnut chart data\n },\n ],\n};\n", "google": { "callback": "gmapReady", "key": "" @@ -279,8 +287,14 @@ data: "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -576,8 +590,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -646,8 +659,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -716,8 +728,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -786,8 +797,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -856,8 +866,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -959,8 +968,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1060,8 +1068,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3112,7 +3119,8 @@ data: "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 1, + "version": 4, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index d762bc19..93ef604e 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 106, + "id": 58, "links": [], "liveNow": false, "panels": [ @@ -140,12 +140,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241666) AND vul_last_modified_date <= toDateTime(1712328066) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -247,12 +248,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241678) AND vul_last_modified_date <= toDateTime(1712328078) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -354,12 +356,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241603) AND vul_last_modified_date <= toDateTime(1712328003) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -461,12 +464,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241618) AND vul_last_modified_date <= toDateTime(1712328018) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -498,8 +502,7 @@ data: "mode": "absolute", "steps": [ { - "color": "light-blue", - "value": null + "color": "light-blue" } ] } @@ -549,12 +552,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241558) AND vul_last_modified_date <= toDateTime(1712327958) AND vul_severity = 'LOW'", "refId": "A", "round": "0s", "skip_comments": true @@ -586,8 +590,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -637,12 +640,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241583) AND vul_last_modified_date <= toDateTime(1712327983) AND vul_severity = 'MEDIUM'", "refId": "A", "round": "0s", "skip_comments": true @@ -674,8 +678,7 @@ data: "mode": "absolute", "steps": [ { - "color": "super-light-orange", - "value": null + "color": "super-light-orange" } ] } @@ -725,12 +728,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241526) AND vul_last_modified_date <= toDateTime(1712327926) AND vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true @@ -762,8 +766,7 @@ data: "mode": "absolute", "steps": [ { - "color": "red", - "value": null + "color": "red" } ] } @@ -813,12 +816,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241543) AND vul_last_modified_date <= toDateTime(1712327943) AND vul_severity = 'CRITICAL'", "refId": "A", "round": "0s", "skip_comments": true @@ -850,8 +854,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -923,8 +926,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -978,12 +980,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", - "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", + "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_id", + "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241498) AND vul_last_modified_date <= toDateTime(1712327898)\nGROUP BY vul_id", "refId": "A", "round": "0s", "skip_comments": true @@ -1016,8 +1019,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -1089,8 +1091,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -2407,7 +2408,8 @@ data: "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 13, + "version": 2, "weekStart": "" } + {{- end }} \ No newline at end of file diff --git a/grafana/azure-dashboard.json b/grafana/azure-dashboard.json index bea62afc..b90566c4 100644 --- a/grafana/azure-dashboard.json +++ b/grafana/azure-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 33, + "id": 53, "links": [], "liveNow": false, "panels": [ @@ -40,11 +40,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -55,8 +56,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -99,11 +106,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -114,8 +122,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -158,11 +172,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,8 +188,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -212,7 +233,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -282,7 +304,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -359,7 +382,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -415,7 +439,11 @@ "templating": { "list": [ { - "current": {}, + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" @@ -434,7 +462,11 @@ "type": "query" }, { - "current": {}, + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, "datasource": { "type": "vertamedia-clickhouse-datasource", "uid": "vertamedia-clickhouse-datasource" @@ -462,6 +494,6 @@ "timezone": "", "title": "Azure", "uid": "dd66838a-ffda-4de2-944f-1828d1671fc9", - "version": 1, + "version": 2, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/bitBucket-dashboard.json b/grafana/bitBucket-dashboard.json index 389c001c..63d77351 100644 --- a/grafana/bitBucket-dashboard.json +++ b/grafana/bitBucket-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 34, + "id": 60, "links": [], "liveNow": false, "panels": [ @@ -40,11 +40,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -55,8 +56,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -99,11 +106,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -114,8 +122,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -158,11 +172,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,8 +188,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -212,7 +233,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -282,7 +304,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -440,8 +463,8 @@ { "current": { "selected": false, - "text": "", - "value": "" + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -470,6 +493,6 @@ "timezone": "", "title": "BitBucket", "uid": "a7772dd5-76c7-48f3-8462-b39fbc20941c", - "version": 1, + "version": 2, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/giTea-dashboard.json b/grafana/giTea-dashboard.json index da0e63a2..9dccd2af 100644 --- a/grafana/giTea-dashboard.json +++ b/grafana/giTea-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 35, + "id": 61, "links": [], "liveNow": false, "panels": [ @@ -40,11 +40,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -55,8 +56,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -99,11 +106,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -114,8 +122,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -158,11 +172,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,8 +188,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -212,7 +233,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -282,7 +304,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -359,7 +382,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -416,9 +440,13 @@ "list": [ { "current": { - "selected": false, - "text": "All", - "value": "$__all" + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -440,8 +468,8 @@ { "current": { "selected": false, - "text": "", - "value": "" + "text": "All", + "value": "$__all" }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -470,6 +498,6 @@ "timezone": "", "title": "GiTea", "uid": "a1c6d705-91b0-4718-99b2-d93b0221bca9", - "version": 1, + "version": 2, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/gitHub-dashboard.json b/grafana/gitHub-dashboard.json index 8b3930db..2c3a5786 100644 --- a/grafana/gitHub-dashboard.json +++ b/grafana/gitHub-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 37, + "id": 57, "links": [], "liveNow": false, "panels": [ @@ -40,11 +40,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", "google": { "callback": "gmapReady", "key": "" @@ -55,9 +56,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, - "pluginVersion": "10.0.3", + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -100,11 +106,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -115,8 +122,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -159,11 +172,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -174,8 +188,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -218,11 +238,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "getOption": "const values = context.panel.data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", "google": { "callback": "gmapReady", "key": "" @@ -233,8 +254,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -277,11 +304,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "const values = data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", + "getOption": "const values = context.panel.data.series[0].fields[0].values;\n\nreturn {\n series: [\n {\n type: 'liquidFill',\n radius: '90%',\n data: values, // Use the raw values here\n label: {\n formatter: '{a|{c}}',\n rich: {\n a: {\n color: '#000', // You can set the text color here\n },\n },\n },\n tooltip: {\n formatter: '{a}: {c}', // This formatter displays the raw values\n },\n },\n ],\n};\n", "google": { "callback": "gmapReady", "key": "" @@ -292,8 +320,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -338,7 +372,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -395,9 +430,13 @@ "list": [ { "current": { - "selected": false, - "text": "", - "value": "" + "selected": true, + "text": [ + "ahinvinith" + ], + "value": [ + "ahinvinith" + ] }, "datasource": { "type": "vertamedia-clickhouse-datasource", @@ -455,4 +494,4 @@ "uid": "ef91218c-94cb-48b1-be1d-0bafe848b75c", "version": 2, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/gitLab-dashboard.json b/grafana/gitLab-dashboard.json index 00c49d0b..b6517a4c 100644 --- a/grafana/gitLab-dashboard.json +++ b/grafana/gitLab-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 36, + "id": 59, "links": [], "liveNow": false, "panels": [ @@ -40,11 +40,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const eventTypes = data.series[0].fields[0].values;\n const authors = data.series[0].fields[1].values;\n const repoNames = data.series[0].fields[2].values;\n const total = data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", + "getOption": "// Check if data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const eventTypes = context.panel.data.series[0].fields[0].values;\n const authors = context.panel.data.series[0].fields[1].values;\n const repoNames = context.panel.data.series[0].fields[2].values;\n const total = context.panel.data.series[0].fields[3].values; // Assuming you have a field named \"Total\"\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n authors.forEach((author, index) => {\n const sourceNode = {\n name: author,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const eventTypeNode = {\n name: eventTypes[index],\n category: 1, // Category for eventType nodes\n symbolSize: 40, // Size for eventType nodes\n };\n\n const repoNode = {\n name: repoNames[index],\n category: 2, // Category for repo nodes\n symbolSize: 30, // Size for repo nodes\n };\n\n const totalNode = {\n name: `Total: ${total[index]}`, // Assuming you have an array \"total\"\n category: 3, // Category for total nodes\n symbolSize: 20, // Size for total nodes\n };\n\n // Ensure source, eventType, repo, and total nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === eventTypeNode.name)) {\n nodes.push(eventTypeNode);\n }\n\n if (!nodes.some((node) => node.name === repoNode.name)) {\n nodes.push(repoNode);\n }\n\n if (!nodes.some((node) => node.name === totalNode.name)) {\n nodes.push(totalNode);\n }\n\n // Create links between author, eventType, repo, and total nodes\n links.push({\n source: author,\n target: eventTypes[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: repoNames[index],\n });\n\n links.push({\n source: eventTypes[index],\n target: totalNode.name,\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Authors',\n },\n {\n name: 'Event Type',\n },\n {\n name: 'Repo Names',\n },\n {\n name: 'Total', // Add a category for \"Total\" nodes\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Authors', 'Event Types', 'Repo Names', 'Total'], // Add \"Total\" to legend data\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: { // Add textStyle property to configure text style\n color: '#000', // Set the text color to a brighter color, such as white (#FFF)\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n\n // Increase the size of arrow marks\n edgeSymbolSize: [12, 12], // Set the arrow size here\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display a custom message when data is not available\n const option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n\n return option;\n}", "google": { "callback": "gmapReady", "key": "" @@ -55,8 +56,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -99,11 +106,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let options; // Initialize the options variable\n\nif (!data || !data.series || data.series.length === 0 || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", + "getOption": "let options; // Initialize the options variable\n\nif (!context.panel.data || !context.panel.data.series || context.panel.data.series.length === 0 || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n options = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract Author and Push_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const pushEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Push_Events').values;\n\n // Create the ECharts options\n options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Push_Events'],\n orient: 'vertical', // Change the orientation to vertical\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: pushEvents,\n type: 'line',\n areaStyle: {\n color: 'rgba(0, 128, 255, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'blue', // Set the line color\n },\n name: 'Push_Events',\n },\n ],\n };\n}\n\nreturn options;\n", "google": { "callback": "gmapReady", "key": "" @@ -114,8 +122,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -158,11 +172,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\n// Define a default options object\nconst defaultOptions = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n};\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = defaultOptions;\n} else {\n // Extract Author and Merge_Events data from the series\n const categories = context.panel.data.series[0].fields.find((f) => f.name === 'Author').values;\n const mergeEvents = context.panel.data.series[0].fields.find((f) => f.name === 'Merge_Events').values;\n\n // Create the ECharts options\n const options = {\n grid: {\n bottom: '3%',\n containLabel: true,\n left: '3%',\n right: '4%',\n top: '4%',\n },\n toolbox: {\n right: '5%', // Adjust the right margin to position it on the top right\n top: '0%', // Adjust the top margin to position it on the top right\n feature: {\n dataZoom: {\n yAxisIndex: 'none',\n },\n restore: {},\n },\n },\n tooltip: {\n trigger: 'axis',\n axisPointer: {\n type: 'shadow',\n },\n },\n xAxis: {\n type: 'category',\n data: categories,\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Merge_Events'],\n orient: 'vertical',\n left: '5%',\n top: '5%',\n },\n series: [\n {\n data: mergeEvents,\n type: 'line',\n name: 'Merge_Events',\n areaStyle: {\n color: 'rgba(255, 0, 0, 0.3)', // Set the area (shadow) color\n },\n lineStyle: {\n color: 'red', // Set the line color\n },\n },\n ],\n };\n\n option = options; // Assign the options to the outer variable\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -173,8 +188,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -212,7 +233,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -282,7 +304,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -359,7 +382,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -474,6 +498,6 @@ "timezone": "", "title": "GitLab", "uid": "ec8b9cb1-f9ae-4139-b270-4824b6508eff", - "version": 1, + "version": 2, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/kubeData-dashboard.json b/grafana/kubeData-dashboard.json index 5b351bf5..998bc514 100644 --- a/grafana/kubeData-dashboard.json +++ b/grafana/kubeData-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 146, + "id": 56, "links": [], "liveNow": false, "panels": [ @@ -46,11 +46,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values;\n const namespaces = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const pods = data.series[0].fields[3].values;\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const reason = reasons[i];\n const pod = pods[i];\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const reasonNode = { name: reason, children: [{ name: `Count: ${pod}` }] };\n namespaceNode.children.push(reasonNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const namespaces = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const pods = context.panel.data.series[0].fields[3].values;\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const reason = reasons[i];\n const pod = pods[i];\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const reasonNode = { name: reason, children: [{ name: `Count: ${pod}` }] };\n namespaceNode.children.push(reasonNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;", "google": { "callback": "gmapReady", "key": "" @@ -61,8 +62,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -105,11 +112,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const reasons = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Check if reasons and counts are defined and not empty\n if (!reasons || !counts || reasons.length === 0 || counts.length === 0) {\n // Display a message when no data is available\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n } else {\n // Data is available, proceed with the chart creation\n // Create an array of data items, each containing name and value\n const seriesData = reasons.map((reason, index) => ({\n name: reason,\n value: counts[index],\n }));\n\n // Define a custom color for the bars\n const customColor = 'rgb(0, 123, 255)'; // Change this to your desired color\n\n // Apache ECharts option\n option = {\n xAxis: {\n type: 'category',\n data: reasons, // Use the reasons directly for xAxis data\n axisLabel: {\n interval: 0, // Display all labels on the xAxis\n },\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Pods'], // Legend name\n left: 'left', // Position the legend on the left side\n bottom: 'bottom', // Position the legend at the bottom\n },\n series: [\n {\n name: 'Pods', // Series name for the legend\n data: seriesData,\n type: 'bar',\n label: {\n show: true,\n position: 'top',\n formatter: '{c}',\n },\n itemStyle: {\n barBorderRadius: [5, 5, 0, 0], // Adjust the values to control the curvature\n color: customColor, // Set the custom color for the bars\n },\n },\n ],\n };\n }\n}\n\nreturn option;", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const reasons = context.panel.data.series[0].fields[0].values;\n const counts = context.panel.data.series[0].fields[1].values;\n\n // Check if reasons and counts are defined and not empty\n if (!reasons || !counts || reasons.length === 0 || counts.length === 0) {\n // Display a message when no data is available\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n } else {\n // Data is available, proceed with the chart creation\n // Create an array of data items, each containing name and value\n const seriesData = reasons.map((reason, index) => ({\n name: reason,\n value: counts[index],\n }));\n\n // Define a custom color for the bars\n const customColor = 'rgb(0, 123, 255)'; // Change this to your desired color\n\n // Apache ECharts option\n option = {\n xAxis: {\n type: 'category',\n data: reasons, // Use the reasons directly for xAxis data\n axisLabel: {\n interval: 0, // Display all labels on the xAxis\n },\n },\n yAxis: {\n type: 'value',\n },\n legend: {\n data: ['Pods'], // Legend name\n left: 'left', // Position the legend on the left side\n bottom: 'bottom', // Position the legend at the bottom\n },\n series: [\n {\n name: 'Pods', // Series name for the legend\n data: seriesData,\n type: 'bar',\n label: {\n show: true,\n position: 'top',\n formatter: '{c}',\n },\n itemStyle: {\n barBorderRadius: [5, 5, 0, 0], // Adjust the values to control the curvature\n color: customColor, // Set the custom color for the bars\n },\n },\n ],\n };\n }\n}\n\nreturn option;", "google": { "callback": "gmapReady", "key": "" @@ -120,8 +128,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -164,11 +178,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const counts = context.panel.data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", "google": { "callback": "gmapReady", "key": "" @@ -179,8 +194,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -223,11 +244,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const counts = context.panel.data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);", "google": { "callback": "gmapReady", "key": "" @@ -238,8 +260,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -282,11 +310,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const counts = data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);\n\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n\n // Define the data from your JSON\n const clusterNames = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const counts = context.panel.data.series[0].fields[3].values;\n\n // Create the Sankey chart configuration\n option = {\n series: {\n type: 'sankey',\n layout: 'none',\n emphasis: {\n focus: 'adjacency',\n },\n data: [],\n links: [],\n },\n tooltip: {\n trigger: 'item',\n formatter: (params) => {\n if (params.dataType === 'node') {\n return params.name;\n }\n if (params.dataType === 'edge') {\n return `Count: ${counts[params.dataIndex]}`; // Display count values\n }\n return '';\n },\n },\n };\n\n // Create nodes for ClusterName, Kind, and Reason\n const uniqueClusterNames = Array.from(new Set(clusterNames));\n const uniqueKinds = Array.from(new Set(kinds));\n const uniqueReasons = Array.from(new Set(reasons));\n\n uniqueClusterNames.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueKinds.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n uniqueReasons.forEach((name, index) => {\n option.series.data.push({\n name: name,\n });\n });\n\n\n // Create links from Kind to Reason\n kinds.forEach((kind, index) => {\n const sourceIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kind);\n const targetIndex = 1 * uniqueClusterNames.length + uniqueKinds.length + uniqueReasons.indexOf(reasons[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: counts[index], // Use count values\n });\n });\n\n // Create links from ClusterName to Kind\n clusterNames.forEach((clusterName, index) => {\n const sourceIndex = uniqueClusterNames.indexOf(clusterName);\n const targetIndex = uniqueClusterNames.length + uniqueKinds.indexOf(kinds[index]);\n option.series.links.push({\n source: sourceIndex,\n target: targetIndex,\n value: 1,\n });\n });\n}\n\n\nreturn option;\n// Render the chart\nmyChart.setOption(option);\n\n", "google": { "callback": "gmapReady", "key": "" @@ -297,8 +326,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -341,11 +376,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON\n const clusters = data.series[0].fields[0].values;\n const hosts = data.series[0].fields[1].values;\n const reasons = data.series[0].fields[2].values;\n const eventTimes = data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Root', // You can customize the name of the root node if needed\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly as root nodes\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON\n const clusters = context.panel.data.series[0].fields[0].values;\n const hosts = context.panel.data.series[0].fields[1].values;\n const reasons = context.panel.data.series[0].fields[2].values;\n const eventTimes = context.panel.data.series[0].fields[3].values;\n\n // Create a hierarchical structure for the tree chart starting with ClusterName\n const hierarchy = {\n name: 'Root', // You can customize the name of the root node if needed\n children: [],\n };\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i];\n const host = hosts[i];\n const reason = reasons[i];\n const eventTime = eventTimes[i];\n\n // Find or create the cluster node\n let clusterNode = hierarchy.children.find((node) => node.name === cluster);\n if (!clusterNode) {\n clusterNode = { name: cluster, children: [] };\n hierarchy.children.push(clusterNode);\n }\n\n // Find or create the host node under the cluster\n let hostNode = clusterNode.children.find((node) => node.name === host);\n if (!hostNode) {\n hostNode = { name: host, children: [] };\n clusterNode.children.push(hostNode);\n }\n\n // Find or create the reason node under the host\n let reasonNode = hostNode.children.find((node) => node.name === reason);\n if (!reasonNode) {\n reasonNode = { name: reason, children: [] };\n hostNode.children.push(reasonNode);\n }\n\n // Create the eventTime node under the reason\n reasonNode.children.push({ name: eventTime });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly as root nodes\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%', // Adjust the right margin to provide more space for labels\n symbolSize: 7,\n label: {\n position: 'inside', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'center', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n leaves: {\n label: {\n position: 'right', // Position labels inside the node\n verticalAlign: 'middle',\n align: 'left', // Center-align labels\n fontSize: 15,\n fontWeight: 'bold',\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -356,8 +392,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -400,11 +442,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = data.series[0].fields[0].values; // New column for ClusterName\n const namespaces = data.series[0].fields[1].values;\n const kinds = data.series[0].fields[2].values; // Adjusted index for Kind\n const counts = data.series[0].fields[3].values; // New column for Count\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const clusterNames = context.panel.data.series[0].fields[0].values; // New column for ClusterName\n const namespaces = context.panel.data.series[0].fields[1].values;\n const kinds = context.panel.data.series[0].fields[2].values; // Adjusted index for Kind\n const counts = context.panel.data.series[0].fields[3].values; // New column for Count\n\n // Create a hierarchical structure from the data without a root node\n const hierarchy = {\n name: 'root', // Use 'root' as a placeholder\n children: [],\n };\n\n const seenClusterNames = new Set();\n const seenNamespaces = new Set();\n\n for (let i = 0; i < clusterNames.length; i++) {\n const clusterName = clusterNames[i];\n const namespace = namespaces[i];\n const kind = kinds[i];\n const count = counts[i]; // Get the count value\n\n if (!seenClusterNames.has(clusterName)) {\n seenClusterNames.add(clusterName);\n const clusterNode = { name: clusterName, children: [] };\n hierarchy.children.push(clusterNode);\n seenNamespaces.clear(); // Reset seenNamespaces for each cluster\n }\n\n const clusterNode = hierarchy.children.find((node) => node.name === clusterName);\n\n if (!seenNamespaces.has(namespace)) {\n seenNamespaces.add(namespace);\n const namespaceNode = { name: namespace, children: [] };\n clusterNode.children.push(namespaceNode);\n }\n\n const namespaceNode = clusterNode.children.find((node) => node.name === namespace);\n const kindNode = { name: kind, children: [{ name: `Count: ${count}` }] }; // Include the count as a child node\n namespaceNode.children.push(kindNode);\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: hierarchy.children, // Use the children directly\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'right',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -415,8 +458,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -455,8 +504,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -695,6 +743,6 @@ "timezone": "", "title": "Kubedata", "uid": "Qq-FK1rVz", - "version": 2, + "version": 3, "weekStart": "" } diff --git a/grafana/kuberhealthy-dashboard.json b/grafana/kuberhealthy-dashboard.json index 915b7747..76e2c6e6 100644 --- a/grafana/kuberhealthy-dashboard.json +++ b/grafana/kuberhealthy-dashboard.json @@ -45,7 +45,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = context.panel.data.series[0].fields[0].values;\n const counts = context.panel.data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -113,7 +113,7 @@ "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let option; // Initialize the option variable\n\nif (typeof data === 'undefined' || !data.series || !data.series[0] || !data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = data.series[0].fields[0].values;\n const counts = data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", + "getOption": "let option; // Initialize the option variable\n\nif (typeof context.panel.data === 'undefined' || !context.panel.data.series || !context.panel.data.series[0] || !context.panel.data.series[0].fields) {\n // Data is not available or doesn't have the expected structure\n option = {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n} else {\n // Extract data from your JSON as before\n const namespaces = context.panel.data.series[0].fields[0].values;\n const counts = context.panel.data.series[0].fields[1].values;\n\n // Create a hierarchical structure from the data with a default cluster node\n const hierarchy = {\n name: 'CheckName', // Default cluster node\n children: [],\n };\n\n // Create an object to store namespaces and their counts\n const namespaceCounts = {};\n\n // Populate the namespaceCounts object with namespaces and counts\n for (let i = 0; i < namespaces.length; i++) {\n const namespace = namespaces[i];\n const count = counts[i];\n\n if (!namespaceCounts[namespace]) {\n namespaceCounts[namespace] = count;\n } else {\n namespaceCounts[namespace] += count;\n }\n }\n\n // Create nodes for each namespace and add them as children of the default cluster node\n for (const namespace in namespaceCounts) {\n hierarchy.children.push({\n name: namespace,\n children: [{ name: `${namespaceCounts[namespace]}` }],\n });\n }\n\n // Create the tree chart using ECharts\n option = {\n tooltip: {\n trigger: 'item',\n triggerOn: 'mousemove',\n formatter: function (params) {\n const node = params.data;\n let tooltip = '';\n if (node.column) {\n tooltip += `${node.column}: ${node.name}`;\n } else {\n tooltip += node.name;\n }\n return tooltip;\n },\n },\n series: [\n {\n type: 'tree',\n data: [hierarchy], // Use the hierarchy object as the data\n top: '1%',\n left: '7%',\n bottom: '1%',\n right: '20%',\n symbolSize: 7,\n label: {\n position: 'left',\n verticalAlign: 'middle',\n align: 'centre',\n fontSize: 15, // Increase the text size for regular nodes\n fontWeight: 'bold', // Set the font weight to bold\n },\n leaves: {\n label: {\n position: 'right',\n verticalAlign: 'middle',\n align: 'left',\n fontSize: 15, // Increase the text size for leaves\n fontWeight: 'bold', // Set the font weight to bold\n },\n },\n emphasis: {\n focus: 'descendant',\n },\n expandAndCollapse: true,\n animationDuration: 550,\n animationDurationUpdate: 750,\n },\n ],\n };\n}\n\nreturn option;\n", "google": { "callback": "gmapReady", "key": "" @@ -1199,6 +1199,6 @@ "timezone": "", "title": "KuberHealth", "uid": "d946c53c-8b1d-4e3c-9154-4219165342", - "version": 2, + "version": 3, "weekStart": "" } diff --git a/grafana/kubvizDsahboard.json b/grafana/kubvizDsahboard.json index 374d131b..740b4baa 100644 --- a/grafana/kubvizDsahboard.json +++ b/grafana/kubvizDsahboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 32, + "id": 64, "links": [], "liveNow": false, "panels": [ @@ -194,11 +194,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "// Check if data.series exists\nif (data.series && data.series.length > 0) {\n const reasons = data.series[0].fields[0].values;\n const kinds = data.series[0].fields[1].values;\n const eventTimes = data.series[0].fields[2].values;\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n reasons.forEach((reason, index) => {\n const sourceNode = {\n name: reason,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const kindNode = {\n name: kinds[index],\n category: 1, // Category for kind nodes\n symbolSize: 40, // Size for kind nodes\n };\n\n const eventTimeNode = {\n name: eventTimes[index],\n category: 2, // Category for eventTime nodes\n symbolSize: 20, // Size for eventTime nodes\n };\n\n // Ensure source, kind, and eventTime nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === kindNode.name)) {\n nodes.push(kindNode);\n }\n\n if (!nodes.some((node) => node.name === eventTimeNode.name)) {\n nodes.push(eventTimeNode);\n }\n\n // Create links between reason, kind, and eventTime nodes\n links.push({\n source: reason,\n target: kinds[index],\n });\n\n links.push({\n source: kinds[index],\n target: eventTimes[index],\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Reasons',\n },\n {\n name: 'Nodes',\n },\n {\n name: 'Event Times',\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Reasons', 'Nodes', 'Event Times'],\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: {\n color: '#000',\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n edgeSymbolSize: [12, 12],\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Handle the case when data.series does not exist\n return {};\n}", + "getOption": "// Check if context.panel.data.series exists\nif (context.panel.data.series && context.panel.data.series.length > 0) {\n const reasons = context.panel.data.series[0].fields[0].values;\n const kinds = context.panel.data.series[0].fields[1].values;\n const eventTimes = context.panel.data.series[0].fields[2].values;\n\n // Create nodes and links\n const nodes = [];\n const links = [];\n\n reasons.forEach((reason, index) => {\n const sourceNode = {\n name: reason,\n category: 0, // Category for source nodes\n symbolSize: 60, // Size for source nodes\n };\n\n const kindNode = {\n name: kinds[index],\n category: 1, // Category for kind nodes\n symbolSize: 40, // Size for kind nodes\n };\n\n const eventTimeNode = {\n name: eventTimes[index],\n category: 2, // Category for eventTime nodes\n symbolSize: 20, // Size for eventTime nodes\n };\n\n // Ensure source, kind, and eventTime nodes are unique before adding them\n if (!nodes.some((node) => node.name === sourceNode.name)) {\n nodes.push(sourceNode);\n }\n\n if (!nodes.some((node) => node.name === kindNode.name)) {\n nodes.push(kindNode);\n }\n\n if (!nodes.some((node) => node.name === eventTimeNode.name)) {\n nodes.push(eventTimeNode);\n }\n\n // Create links between reason, kind, and eventTime nodes\n links.push({\n source: reason,\n target: kinds[index],\n });\n\n links.push({\n source: kinds[index],\n target: eventTimes[index],\n });\n });\n\n // Create categories for nodes\n const categories = [\n {\n name: 'Reasons',\n },\n {\n name: 'Nodes',\n },\n {\n name: 'Event Times',\n },\n ];\n\n // Create ECharts option\n const option = {\n tooltip: {\n trigger: 'item',\n formatter: '{b}',\n },\n legend: {\n x: 'left',\n data: ['Reasons', 'Nodes', 'Event Times'],\n },\n series: [\n {\n type: 'graph',\n layout: 'circular',\n roam: true,\n label: {\n show: true,\n textStyle: {\n color: '#000',\n },\n },\n force: {\n repulsion: 100,\n gravity: 0.1,\n edgeLength: 150,\n },\n data: nodes,\n links: links,\n draggable: true,\n categories: categories,\n edgeSymbol: [\"none\", \"arrow\"],\n edgeSymbolSize: [12, 12],\n lineStyle: {\n color: \"#000000\",\n curveness: 0,\n opacity: 0.3,\n },\n },\n ],\n };\n\n // Return the ECharts option\n return option;\n} else {\n // Display \"Data not available\" in the panel\n return {\n title: {\n text: 'Data not available',\n textStyle: {\n fontSize: 24,\n fontWeight: 'bold',\n },\n left: 'center',\n top: 'middle',\n },\n };\n}\n", "google": { "callback": "gmapReady", "key": "" @@ -209,8 +210,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -253,11 +260,12 @@ "format": "auto", "height": 600 }, + "editorMode": "code", "gaode": { "key": "", "plugin": "AMap.Scale,AMap.ToolBar" }, - "getOption": "let kind = [];\nlet resources = [];\n\ndata.series.map((s) => {\n kind = s.fields.find((f) => f.name === 'Kind').values;\n resources = s.fields.find((f) => f.name === 'Resources').values;\n});\n\n// Create an empty array to store doughnut chart data\nconst doughnutChartData = [];\n\n// Define colors for doughnut slices\nconst doughnutSliceColors = ['#235894', '#FFFF00', '#FF0000', '#00FF00', '#FFA500'];\n\n// Map severity and counts to doughnut chart data\nkind.forEach((kinddata, index) => {\n doughnutChartData.push({\n value: resources[index],\n name: kinddata,\n itemStyle: {\n borderRadius: [10, 10, 10, 10], // Add rounded corners\n color: doughnutSliceColors[index % doughnutSliceColors.length],\n borderWidth: 2,\n borderColor: '#fff',\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {\n trigger: 'item',\n },\n legend: {\n top: '5%',\n left: 'center',\n },\n series: [\n {\n name: 'Access From',\n type: 'pie',\n radius: ['40%', '70%'],\n avoidLabelOverlap: false,\n label: {\n show: false,\n position: 'center',\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 40,\n fontWeight: 'bold',\n },\n },\n labelLine: {\n show: false,\n },\n data: doughnutChartData, // Use the modified doughnut chart data\n },\n ],\n};", + "getOption": "let kind = [];\nlet resources = [];\n\ncontext.panel.data.series.forEach((s) => {\n const kindField = s.fields.find((f) => f.name === 'Kind');\n const resourcesField = s.fields.find((f) => f.name === 'Resources');\n if (kindField && resourcesField) {\n kind = kindField.values;\n resources = resourcesField.values;\n }\n});\n\n// Create an empty array to store doughnut chart data\nconst doughnutChartData = [];\n\n// Define colors for doughnut slices\nconst doughnutSliceColors = ['#235894', '#FFFF00', '#FF0000', '#00FF00', '#FFA500'];\n\n// Map kind and resources counts to doughnut chart data\nkind.forEach((kinddata, index) => {\n doughnutChartData.push({\n value: resources[index],\n name: kinddata,\n clusterName: context.panel.data.series[0].fields[0].values[index], // Extract cluster name\n itemStyle: {\n borderRadius: [10, 10, 10, 10], // Add rounded corners\n color: doughnutSliceColors[index % doughnutSliceColors.length],\n borderWidth: 2,\n borderColor: '#fff',\n },\n });\n});\n\nreturn {\n backgroundColor: '#FFFFFF', // Set the background color to white\n tooltip: {\n trigger: 'item',\n formatter: function (params) {\n return `Resource From
${params.data.clusterName} ${params.value}`;\n },\n },\n legend: {\n top: '5%',\n left: 'center',\n },\n series: [\n {\n name: '',\n type: 'pie',\n radius: ['40%', '70%'],\n avoidLabelOverlap: false,\n label: {\n show: false,\n position: 'center',\n },\n emphasis: {\n label: {\n show: true,\n fontSize: 40,\n fontWeight: 'bold',\n },\n },\n labelLine: {\n show: false,\n },\n data: doughnutChartData, // Use the modified doughnut chart data\n },\n ],\n};\n", "google": { "callback": "gmapReady", "key": "" @@ -268,8 +276,14 @@ "config": "{}", "height": 400, "name": "default" + }, + "visualEditor": { + "code": "return {\n dataset: context.editor.dataset,\n series: context.editor.series,\n xAxis: {\n type: 'time',\n },\n yAxis: {\n type: 'value',\n min: 'dataMin',\n },\n}\n", + "dataset": [], + "series": [] } }, + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -565,8 +579,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -635,8 +648,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -705,8 +717,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -775,8 +786,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -845,8 +855,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -948,8 +957,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1049,8 +1057,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3101,6 +3108,6 @@ "timezone": "", "title": "Kubviz Dashboard", "uid": "eT4fox94z", - "version": 1, + "version": 4, "weekStart": "" -} \ No newline at end of file +} diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index db35cd83..52b06fab 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 106, + "id": 58, "links": [], "liveNow": false, "panels": [ @@ -129,12 +129,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241666) AND vul_last_modified_date <= toDateTime(1712328066) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -236,12 +237,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241678) AND vul_last_modified_date <= toDateTime(1712328078) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -343,12 +345,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241603) AND vul_last_modified_date <= toDateTime(1712328003) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -450,12 +453,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241618) AND vul_last_modified_date <= toDateTime(1712328018) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -487,8 +491,7 @@ "mode": "absolute", "steps": [ { - "color": "light-blue", - "value": null + "color": "light-blue" } ] } @@ -538,12 +541,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'LOW'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241558) AND vul_last_modified_date <= toDateTime(1712327958) AND vul_severity = 'LOW'", "refId": "A", "round": "0s", "skip_comments": true @@ -575,8 +579,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -626,12 +629,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'MEDIUM'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241583) AND vul_last_modified_date <= toDateTime(1712327983) AND vul_severity = 'MEDIUM'", "refId": "A", "round": "0s", "skip_comments": true @@ -663,8 +667,7 @@ "mode": "absolute", "steps": [ { - "color": "super-light-orange", - "value": null + "color": "super-light-orange" } ] } @@ -714,12 +717,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'HIGH'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241526) AND vul_last_modified_date <= toDateTime(1712327926) AND vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true @@ -751,8 +755,7 @@ "mode": "absolute", "steps": [ { - "color": "red", - "value": null + "color": "red" } ] } @@ -802,12 +805,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_severity = 'CRITICAL'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241543) AND vul_last_modified_date <= toDateTime(1712327943) AND vul_severity = 'CRITICAL'", "refId": "A", "round": "0s", "skip_comments": true @@ -839,8 +843,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -912,8 +915,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -967,12 +969,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", - "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nGROUP BY vul_id", + "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_id", + "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241498) AND vul_last_modified_date <= toDateTime(1712327898)\nGROUP BY vul_id", "refId": "A", "round": "0s", "skip_comments": true @@ -1005,8 +1008,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -1078,8 +1080,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" } ] } @@ -2396,6 +2397,6 @@ "timezone": "", "title": "Trivy", "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", - "version": 13, + "version": 2, "weekStart": "" -} \ No newline at end of file +} From db1c198d362fd93fa7f4446408a0f09c63655602 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 8 Apr 2024 13:16:21 +0530 Subject: [PATCH 254/263] Fix: github action failure --- .github/labeler.yml | 36 ++++++++++++++++++++++++ .github/workflows/agent-container-pr.yml | 3 ++ .github/workflows/agent-git-pr.yml | 3 ++ .github/workflows/agent-kubviz-pr.yml | 3 ++ .github/workflows/client-pr.yml | 3 ++ .github/workflows/migration-pr.yml | 3 ++ 6 files changed, 51 insertions(+) create mode 100644 .github/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..45fa1731 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,36 @@ +# Add 'Documentation' label to any changes within 'docs' folder or any subfolders +Documentation: +- changed-files: + - any-glob-to-any-file: docs/** + +# Add 'Documentation' label to any file changes within 'docs' folder +Documentation: +- changed-files: + - any-glob-to-any-file: docs/* + +# Add 'Documentation' label to any file changes within 'docs' or 'guides' folders +Documentation: +- changed-files: + - any-glob-to-any-file: + - docs/* + - guides/* + +# Add 'Documentation' label to any change to .md files within the entire repository +Documentation: +- changed-files: + - any-glob-to-any-file: '**/*.md' + +# Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder +source: +- all: + - changed-files: + - any-glob-to-any-file: 'src/**/*' + - all-globs-to-all-files: '!src/docs/*' + +# Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name +feature: + - head-branch: ['^feature', 'feature'] + +# Add 'release' label to any PR that is opened against the `main` branch +release: + - base-branch: 'main' \ No newline at end of file diff --git a/.github/workflows/agent-container-pr.yml b/.github/workflows/agent-container-pr.yml index 5c61e536..a0f0e1ad 100644 --- a/.github/workflows/agent-container-pr.yml +++ b/.github/workflows/agent-container-pr.yml @@ -2,6 +2,9 @@ name: Container Agent Docker Image CI on: pull_request: + paths-ignore: + - '**.md' + - 'charts/**' branches: - 'main' diff --git a/.github/workflows/agent-git-pr.yml b/.github/workflows/agent-git-pr.yml index cd2f294c..17a15552 100644 --- a/.github/workflows/agent-git-pr.yml +++ b/.github/workflows/agent-git-pr.yml @@ -2,6 +2,9 @@ name: Git Agent Docker Image CI on: pull_request: + paths-ignore: + - '**.md' + - 'charts/**' branches: - 'main' diff --git a/.github/workflows/agent-kubviz-pr.yml b/.github/workflows/agent-kubviz-pr.yml index cc0cf561..81233d39 100644 --- a/.github/workflows/agent-kubviz-pr.yml +++ b/.github/workflows/agent-kubviz-pr.yml @@ -2,6 +2,9 @@ name: Agent Docker Image CI on: pull_request: + paths-ignore: + - '**.md' + - 'charts/**' branches: - 'main' diff --git a/.github/workflows/client-pr.yml b/.github/workflows/client-pr.yml index 870686a1..623ecad7 100644 --- a/.github/workflows/client-pr.yml +++ b/.github/workflows/client-pr.yml @@ -2,6 +2,9 @@ name: Client Docker Image CI on: pull_request: + paths-ignore: + - '**.md' + - 'charts/**' branches: - 'main' diff --git a/.github/workflows/migration-pr.yml b/.github/workflows/migration-pr.yml index 3d582691..2bcd5997 100644 --- a/.github/workflows/migration-pr.yml +++ b/.github/workflows/migration-pr.yml @@ -2,6 +2,9 @@ name: Migration Docker Image CI on: pull_request: + paths-ignore: + - '**.md' + - 'charts/**' branches: - 'main' From 34eb70f503191b330f586680b71bc45a37dffa7b Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 8 Apr 2024 13:24:06 +0530 Subject: [PATCH 255/263] Fix: github action failure --- .github/labeler.yml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 45fa1731..7f3d0407 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,36 +1,36 @@ # Add 'Documentation' label to any changes within 'docs' folder or any subfolders Documentation: -- changed-files: - - any-glob-to-any-file: docs/** + - changed-files: + - any-glob-to-any-file: docs/** # Add 'Documentation' label to any file changes within 'docs' folder -Documentation: -- changed-files: - - any-glob-to-any-file: docs/* +Documentation_docs: + - changed-files: + - any-glob-to-any-file: docs/* # Add 'Documentation' label to any file changes within 'docs' or 'guides' folders -Documentation: -- changed-files: - - any-glob-to-any-file: - - docs/* - - guides/* +Documentation_guides: + - changed-files: + - any-glob-to-any-file: + - docs/* + - guides/* # Add 'Documentation' label to any change to .md files within the entire repository -Documentation: -- changed-files: - - any-glob-to-any-file: '**/*.md' +Documentation_md: + - changed-files: + - any-glob-to-any-file: '**/*.md' # Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder source: -- all: - - changed-files: - - any-glob-to-any-file: 'src/**/*' - - all-globs-to-all-files: '!src/docs/*' + - all: + - changed-files: + - any-glob-to-any-file: 'src/**/*' + - all-globs-to-all-files: '!src/docs/*' # Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name feature: - - head-branch: ['^feature', 'feature'] + - head-branch: ['^feature', 'feature'] # Add 'release' label to any PR that is opened against the `main` branch release: - - base-branch: 'main' \ No newline at end of file + - base-branch: 'main' From 4e90f0e862bb62e28823df6328fd1f6f50e556fd Mon Sep 17 00:00:00 2001 From: Akash LM Date: Mon, 8 Apr 2024 13:43:08 +0530 Subject: [PATCH 256/263] Fix: github action failure --- .github/labeler.yml | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 7f3d0407..ffeada5d 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,31 +1,20 @@ # Add 'Documentation' label to any changes within 'docs' folder or any subfolders Documentation: - changed-files: - - any-glob-to-any-file: docs/** + - any-glob-to-any-file: + - 'docs/**' # Add 'Documentation' label to any file changes within 'docs' folder Documentation_docs: - changed-files: - - any-glob-to-any-file: docs/* - -# Add 'Documentation' label to any file changes within 'docs' or 'guides' folders -Documentation_guides: - - changed-files: - - any-glob-to-any-file: - - docs/* - - guides/* + - any-glob-to-any-file: + - 'docs/*' # Add 'Documentation' label to any change to .md files within the entire repository Documentation_md: - changed-files: - - any-glob-to-any-file: '**/*.md' - -# Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder -source: - - all: - - changed-files: - - any-glob-to-any-file: 'src/**/*' - - all-globs-to-all-files: '!src/docs/*' + - any-glob-to-any-file: + - '**/*.md' # Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name feature: From d0a468c440af7a6cf41b78ff6d6c24663da8e4e3 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Tue, 9 Apr 2024 15:35:13 +0530 Subject: [PATCH 257/263] feat: Add ci.yaml workflow --- .ci.yml | 30 ++++++++++++++++++++++++++++ .github/workflows/ci.yaml | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 .ci.yml create mode 100644 .github/workflows/ci.yaml diff --git a/.ci.yml b/.ci.yml new file mode 100644 index 00000000..7d6d2965 --- /dev/null +++ b/.ci.yml @@ -0,0 +1,30 @@ +run: + timeout: 10m + concurrency: 4 + +concurrency: 4 + +linters: + disable-all: true + enable: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - unused + - misspell + - dupl + - stylecheck + +linters-settings: + gofmt: + simplify: true + dupl: + threshold: 400 + +issues: + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..9b8ea51e --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,41 @@ + +name: ci +on: + push: + branches: + - "*" + - main + pull_request: + +permissions: + contents: write + security-events: write + # Optional: allow read access to pull request. Use with `only-new-issues` option. + pull-requests: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.21' + cache: false + - name: Run tests + run: go test ./... -coverprofile=coverage.out -coverpkg=./... -covermode=atomic + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.54 + args: -v --config=.ci.yml + skip-pkg-cache: true + skip-build-cache: true + + - name: Static check + uses: dominikh/staticcheck-action@v1.3.0 + with: + version: "2023.1.6" + install-go: false + cache-key: '1.21' From 1a7d5e6d2f78a758ddbafc61cb7fd3341a0459a5 Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 16 Apr 2024 11:36:00 +0530 Subject: [PATCH 258/263] added trivy event time --- client/pkg/clickhouse/db_client.go | 3 ++- client/pkg/clickhouse/statements.go | 4 ++-- sql/0000011_trivyimage.up.sql | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/pkg/clickhouse/db_client.go b/client/pkg/clickhouse/db_client.go index 065631f6..7415a378 100644 --- a/client/pkg/clickhouse/db_client.go +++ b/client/pkg/clickhouse/db_client.go @@ -799,7 +799,7 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { _, span := tracer.Start(opentelemetry.BuildContext(ctx), "InsertTrivyImageMetrics") span.SetAttributes(attribute.String("trivy-image-client", "insert")) defer span.End() - + currentTime := time.Now().UTC() for _, result := range metrics.Report.Results { for _, vulnerability := range result.Vulnerabilities { tx, err := c.conn.Begin() @@ -830,6 +830,7 @@ func (c *DBClient) InsertTrivyImageMetrics(metrics model.TrivyImage) { vulnerability.Severity, vulnerability.PublishedDate, vulnerability.LastModifiedDate, + currentTime, ); err != nil { log.Fatal(err) } diff --git a/client/pkg/clickhouse/statements.go b/client/pkg/clickhouse/statements.go index 22406fbe..c75571c3 100644 --- a/client/pkg/clickhouse/statements.go +++ b/client/pkg/clickhouse/statements.go @@ -227,10 +227,10 @@ const clickhouseExperimental DBStatement = `SET allow_experimental_object_type=1 const containerGithubTable DBStatement = `CREATE table IF NOT EXISTS container_github(event JSON) ENGINE = MergeTree ORDER BY tuple();` const InsertKubeScore string = "INSERT INTO kubescore(id,clustername,object_name,kind,apiVersion,name,namespace,target_type,description,path,summary,file_name,file_row,EventTime) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)" const InsertTrivyVul string = "INSERT INTO trivy_vul (id, cluster_name, namespace, kind, name, vul_id, vul_vendor_ids, vul_pkg_id, vul_pkg_name, vul_pkg_path, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?. ?)" -const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +const InsertTrivyImage string = "INSERT INTO trivyimage (id, cluster_name, artifact_name, vul_id, vul_pkg_id, vul_pkg_name, vul_installed_version, vul_fixed_version, vul_title, vul_severity, vul_published_date, vul_last_modified_date,EventTime) VALUES ( ?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)" const InsertTrivyMisconfig string = "INSERT INTO trivy_misconfig (id, cluster_name, namespace, kind, name, misconfig_id, misconfig_avdid, misconfig_type, misconfig_title, misconfig_desc, misconfig_msg, misconfig_query, misconfig_resolution, misconfig_severity, misconfig_status, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertAzureContainerPushEvent DBStatement = "INSERT INTO azurecontainerpush (RegistryURL, RepositoryName, Tag, ImageName, Event, Size, SHAID, EventTime) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?)" const InsertTrivySbom string = "INSERT INTO trivysbom (id, cluster_name, bom_format, serial_number, bom_ref, image_name, component_type, package_url, event_time, other_component_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" const InsertQuayContainerPushEvent DBStatement = "INSERT INTO quaycontainerpush (name, repository, nameSpace, dockerURL, homePage, tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" const InsertJfrogContainerPushEvent DBStatement = "INSERT INTO jfrogcontainerpush (Domain, EventType, RegistryURL, RepositoryName, SHAID, Size, ImageName, Tag, Event, EventTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" -const InsertKuberhealthy string = "INSERT INTO kuberhealthy (CurrentUUID, CheckName, OK, Errors, RunDuration, Namespace, Node, LastRun, AuthoritativePod) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" \ No newline at end of file +const InsertKuberhealthy string = "INSERT INTO kuberhealthy (CurrentUUID, CheckName, OK, Errors, RunDuration, Namespace, Node, LastRun, AuthoritativePod) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)" diff --git a/sql/0000011_trivyimage.up.sql b/sql/0000011_trivyimage.up.sql index acab7047..29e5ad1c 100644 --- a/sql/0000011_trivyimage.up.sql +++ b/sql/0000011_trivyimage.up.sql @@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS trivyimage ( vul_severity String, vul_published_date DateTime('UTC'), vul_last_modified_date DateTime('UTC'), + EventTime DateTime('UTC'), ExpiryDate DateTime DEFAULT now() + INTERVAL {{.TTLValue}} {{.TTLUnit}}, ExportedAt DateTime DEFAULT NULL ) ENGINE = MergeTree() From 98177bf8377347320eafb0077ae0fcaace13282d Mon Sep 17 00:00:00 2001 From: Nithunikzz Date: Tue, 16 Apr 2024 11:51:14 +0530 Subject: [PATCH 259/263] kuberhealthy url fix --- charts/agent/Chart.yaml | 2 +- charts/agent/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 5e80c02f..33444035 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.19 +version: 1.1.20 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 46b6980d..5ac670e1 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -188,7 +188,7 @@ schedule: kuberhealthy: enabled: true pollInterval: "60m" - url: "http://kuberhealthy:8080" + url: "http://kuberhealthy:80" check: podRestarts: enabled: true From c2a9b6b3130a0223f3af5a922bbee007fe79b460 Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Tue, 16 Apr 2024 12:11:30 +0530 Subject: [PATCH 260/263] Fix: Echart Kuberhealth dashboard and TrivyImage timeseries fixed --- charts/client/Chart.yaml | 2 +- .../configmap-kuberhealthy-dashboard.yaml | 35 ++++----- .../templates/configmap-trivy-dashboard.yaml | 72 ++++++++++-------- grafana/kuberhealthy-dashboard.json | 37 ++++------ grafana/trivy-dashboard.json | 74 +++++++++++-------- 5 files changed, 111 insertions(+), 109 deletions(-) diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index 4afef1f2..c88c585c 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.24 +version: 1.1.25 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/client/templates/configmap-kuberhealthy-dashboard.yaml b/charts/client/templates/configmap-kuberhealthy-dashboard.yaml index 70e94c26..cea36528 100644 --- a/charts/client/templates/configmap-kuberhealthy-dashboard.yaml +++ b/charts/client/templates/configmap-kuberhealthy-dashboard.yaml @@ -26,7 +26,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 2, + "id": 41, "links": [], "liveNow": false, "panels": [ @@ -75,7 +75,7 @@ data: "series": [] } }, - "pluginVersion": "5.3.0", + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -143,7 +143,7 @@ data: "series": [] } }, - "pluginVersion": "5.3.0", + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -446,8 +446,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -574,8 +573,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -677,8 +675,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -755,8 +752,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -834,8 +830,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -909,8 +904,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -984,8 +978,7 @@ data: "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1056,8 +1049,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1131,8 +1123,7 @@ data: "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1210,7 +1201,7 @@ data: "timezone": "", "title": "KuberHealth", "uid": "d946c53c-8b1d-4e3c-9154-4219165342", - "version": 3, + "version": 2, "weekStart": "" } diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index 93ef604e..f7c31e58 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -32,7 +32,7 @@ data: "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 58, + "id": 71, "links": [], "liveNow": false, "panels": [ @@ -145,8 +145,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241666) AND vul_last_modified_date <= toDateTime(1712328066) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162544) AND EventTime <= toDateTime(1713248944) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -253,8 +253,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241678) AND vul_last_modified_date <= toDateTime(1712328078) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162563) AND EventTime <= toDateTime(1713248963) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -361,8 +361,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241603) AND vul_last_modified_date <= toDateTime(1712328003) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162505) AND EventTime <= toDateTime(1713248905) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -469,8 +469,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241618) AND vul_last_modified_date <= toDateTime(1712328018) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162524) AND EventTime <= toDateTime(1713248924) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -502,7 +502,8 @@ data: "mode": "absolute", "steps": [ { - "color": "light-blue" + "color": "light-blue", + "value": null } ] } @@ -557,8 +558,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241558) AND vul_last_modified_date <= toDateTime(1712327958) AND vul_severity = 'LOW'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'LOW'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162449) AND EventTime <= toDateTime(1713248849) AND vul_severity = 'LOW'", "refId": "A", "round": "0s", "skip_comments": true @@ -590,7 +591,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -645,8 +647,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241583) AND vul_last_modified_date <= toDateTime(1712327983) AND vul_severity = 'MEDIUM'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'MEDIUM'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162478) AND EventTime <= toDateTime(1713248878) AND vul_severity = 'MEDIUM'", "refId": "A", "round": "0s", "skip_comments": true @@ -678,7 +680,8 @@ data: "mode": "absolute", "steps": [ { - "color": "super-light-orange" + "color": "super-light-orange", + "value": null } ] } @@ -733,8 +736,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241526) AND vul_last_modified_date <= toDateTime(1712327926) AND vul_severity = 'HIGH'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'HIGH'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162400) AND EventTime <= toDateTime(1713248800) AND vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true @@ -766,7 +769,8 @@ data: "mode": "absolute", "steps": [ { - "color": "red" + "color": "red", + "value": null } ] } @@ -821,8 +825,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241543) AND vul_last_modified_date <= toDateTime(1712327943) AND vul_severity = 'CRITICAL'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'CRITICAL'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162429) AND EventTime <= toDateTime(1713248829) AND vul_severity = 'CRITICAL'", "refId": "A", "round": "0s", "skip_comments": true @@ -854,7 +858,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -892,12 +897,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", - "rawQuery": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", + "query": "SELECT image_name, package_url, count(*) AS duplicates\nFROM default.trivysbom\nWHERE $timeFilterByColumn(event_time)\nGROUP BY image_name,package_url\nHAVING count(*) > 1\n", + "rawQuery": "SELECT image_name, package_url, count(*) AS duplicates\nFROM default.trivysbom\nWHERE event_time >= toDateTime(1713162318) AND event_time <= toDateTime(1713248718)\nGROUP BY image_name,package_url\nHAVING count(*) > 1", "refId": "A", "round": "0s", "skip_comments": true @@ -926,7 +932,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -985,8 +992,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_id", - "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241498) AND vul_last_modified_date <= toDateTime(1712327898)\nGROUP BY vul_id", + "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY vul_id", + "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162358) AND EventTime <= toDateTime(1713248758)\nGROUP BY vul_id", "refId": "A", "round": "0s", "skip_comments": true @@ -1019,7 +1026,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -1091,7 +1099,8 @@ data: "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -1125,12 +1134,13 @@ data: "uid": "{{ .Values.datasources.uid }}" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.trivysbom", - "rawQuery": "SELECT * FROM default.trivysbom", + "query": "SELECT * FROM default.trivysbom\nWHERE $timeFilterByColumn(event_time)", + "rawQuery": "SELECT * FROM default.trivysbom\nWHERE event_time >= toDateTime(1713162248) AND event_time <= toDateTime(1713248648)", "refId": "A", "round": "0s", "skip_comments": true diff --git a/grafana/kuberhealthy-dashboard.json b/grafana/kuberhealthy-dashboard.json index 76e2c6e6..46b8bd7d 100644 --- a/grafana/kuberhealthy-dashboard.json +++ b/grafana/kuberhealthy-dashboard.json @@ -15,7 +15,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 2, + "id": 41, "links": [], "liveNow": false, "panels": [ @@ -64,7 +64,7 @@ "series": [] } }, - "pluginVersion": "5.3.0", + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -132,7 +132,7 @@ "series": [] } }, - "pluginVersion": "5.3.0", + "pluginVersion": "6.0.0", "targets": [ { "datasource": { @@ -435,8 +435,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -563,8 +562,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -666,8 +664,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -744,8 +741,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -823,8 +819,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -898,8 +893,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -973,8 +967,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1045,8 +1038,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1120,8 +1112,7 @@ "mode": "percentage", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "orange", @@ -1199,6 +1190,6 @@ "timezone": "", "title": "KuberHealth", "uid": "d946c53c-8b1d-4e3c-9154-4219165342", - "version": 3, + "version": 2, "weekStart": "" -} +} \ No newline at end of file diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index 52b06fab..b0203690 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -21,7 +21,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 58, + "id": 71, "links": [], "liveNow": false, "panels": [ @@ -134,8 +134,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241666) AND vul_last_modified_date <= toDateTime(1712328066) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162544) AND EventTime <= toDateTime(1713248944) AND vul_severity = 'LOW'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -242,8 +242,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241678) AND vul_last_modified_date <= toDateTime(1712328078) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162563) AND EventTime <= toDateTime(1713248963) AND vul_severity = 'HIGH'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -350,8 +350,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241603) AND vul_last_modified_date <= toDateTime(1712328003) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162505) AND EventTime <= toDateTime(1713248905) AND vul_severity = 'MEDIUM'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -458,8 +458,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", - "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241618) AND vul_last_modified_date <= toDateTime(1712328018) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "query": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", + "rawQuery": "SELECT cluster_name, artifact_name, vul_pkg_name, count(vul_severity) AS Counts\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162524) AND EventTime <= toDateTime(1713248924) AND vul_severity = 'CRITICAL'\nGROUP BY cluster_name, artifact_name, vul_pkg_name\nHAVING count(vul_severity) > 1\nORDER BY count(vul_severity) DESC", "refId": "A", "round": "0s", "skip_comments": true @@ -491,7 +491,8 @@ "mode": "absolute", "steps": [ { - "color": "light-blue" + "color": "light-blue", + "value": null } ] } @@ -546,8 +547,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'LOW'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241558) AND vul_last_modified_date <= toDateTime(1712327958) AND vul_severity = 'LOW'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'LOW'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162449) AND EventTime <= toDateTime(1713248849) AND vul_severity = 'LOW'", "refId": "A", "round": "0s", "skip_comments": true @@ -579,7 +580,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -634,8 +636,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'MEDIUM'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241583) AND vul_last_modified_date <= toDateTime(1712327983) AND vul_severity = 'MEDIUM'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'MEDIUM'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162478) AND EventTime <= toDateTime(1713248878) AND vul_severity = 'MEDIUM'", "refId": "A", "round": "0s", "skip_comments": true @@ -667,7 +669,8 @@ "mode": "absolute", "steps": [ { - "color": "super-light-orange" + "color": "super-light-orange", + "value": null } ] } @@ -722,8 +725,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'HIGH'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241526) AND vul_last_modified_date <= toDateTime(1712327926) AND vul_severity = 'HIGH'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'HIGH'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162400) AND EventTime <= toDateTime(1713248800) AND vul_severity = 'HIGH'", "refId": "A", "round": "0s", "skip_comments": true @@ -755,7 +758,8 @@ "mode": "absolute", "steps": [ { - "color": "red" + "color": "red", + "value": null } ] } @@ -810,8 +814,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date) AND vul_severity = 'CRITICAL'", - "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241543) AND vul_last_modified_date <= toDateTime(1712327943) AND vul_severity = 'CRITICAL'", + "query": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime) AND vul_severity = 'CRITICAL'", + "rawQuery": "SELECT cluster_name, artifact_name AS image_name, vul_id, vul_pkg_name, vul_severity\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162429) AND EventTime <= toDateTime(1713248829) AND vul_severity = 'CRITICAL'", "refId": "A", "round": "0s", "skip_comments": true @@ -843,7 +847,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -881,12 +886,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", - "rawQuery": "SELECT image_name, package_name, count(*) AS duplicates\nFROM default.trivysbom\nGROUP BY image_name,package_name\nHAVING count(*) > 1", + "query": "SELECT image_name, package_url, count(*) AS duplicates\nFROM default.trivysbom\nWHERE $timeFilterByColumn(event_time)\nGROUP BY image_name,package_url\nHAVING count(*) > 1\n", + "rawQuery": "SELECT image_name, package_url, count(*) AS duplicates\nFROM default.trivysbom\nWHERE event_time >= toDateTime(1713162318) AND event_time <= toDateTime(1713248718)\nGROUP BY image_name,package_url\nHAVING count(*) > 1", "refId": "A", "round": "0s", "skip_comments": true @@ -915,7 +921,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -974,8 +981,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE $timeFilterByColumn(vul_last_modified_date)\nGROUP BY vul_id", - "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE vul_last_modified_date >= toDateTime(1712241498) AND vul_last_modified_date <= toDateTime(1712327898)\nGROUP BY vul_id", + "query": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE $timeFilterByColumn(EventTime)\nGROUP BY vul_id", + "rawQuery": "SELECT vul_id, count(artifact_name) AS images\nFROM default.trivyimage\nWHERE EventTime >= toDateTime(1713162358) AND EventTime <= toDateTime(1713248758)\nGROUP BY vul_id", "refId": "A", "round": "0s", "skip_comments": true @@ -1008,7 +1015,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -1080,7 +1088,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null } ] } @@ -1114,12 +1123,13 @@ "uid": "vertamedia-clickhouse-datasource" }, "dateTimeType": "DATETIME", + "editorMode": "builder", "extrapolate": true, "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT * FROM default.trivysbom", - "rawQuery": "SELECT * FROM default.trivysbom", + "query": "SELECT * FROM default.trivysbom\nWHERE $timeFilterByColumn(event_time)", + "rawQuery": "SELECT * FROM default.trivysbom\nWHERE event_time >= toDateTime(1713162248) AND event_time <= toDateTime(1713248648)", "refId": "A", "round": "0s", "skip_comments": true @@ -2399,4 +2409,4 @@ "uid": "f9b0a865-f419-410a-b7d9-9a3f79a70d48", "version": 2, "weekStart": "" -} +} \ No newline at end of file From 96c68bf8fcbf1a354112f8249e4f513fc5ea209c Mon Sep 17 00:00:00 2001 From: ahinvinith Date: Tue, 16 Apr 2024 14:11:35 +0530 Subject: [PATCH 261/263] Fixed trivy image panel --- charts/client/templates/configmap-trivy-dashboard.yaml | 5 ++--- grafana/trivy-dashboard.json | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/charts/client/templates/configmap-trivy-dashboard.yaml b/charts/client/templates/configmap-trivy-dashboard.yaml index f7c31e58..fee0c1f4 100644 --- a/charts/client/templates/configmap-trivy-dashboard.yaml +++ b/charts/client/templates/configmap-trivy-dashboard.yaml @@ -1065,9 +1065,8 @@ data: "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", - "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", - "refId": "A", + "query": "SELECT \"cluster_name\", \"EventTime\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE EventTime >= toDateTime(1693581675) AND EventTime <= toDateTime(1694186475)\nORDER BY EventTime DESC", "round": "0s", "skip_comments": true } diff --git a/grafana/trivy-dashboard.json b/grafana/trivy-dashboard.json index b0203690..7241ee58 100644 --- a/grafana/trivy-dashboard.json +++ b/grafana/trivy-dashboard.json @@ -1054,8 +1054,8 @@ "format": "table", "formattedQuery": "SELECT $timeSeries as t, count() FROM $table WHERE $timeFilter GROUP BY t ORDER BY t", "intervalFactor": 1, - "query": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(vul_last_modified_date)\nORDER BY vul_last_modified_date DESC", - "rawQuery": "SELECT \"cluster_name\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE vul_last_modified_date >= toDateTime(1693581675) AND vul_last_modified_date <= toDateTime(1694186475)\nORDER BY vul_last_modified_date DESC", + "query": "SELECT \"cluster_name\", \"EventTime\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE $timeFilterByColumn(EventTime)\nORDER BY EventTime DESC", + "rawQuery": "SELECT \"cluster_name\", \"EventTime\", \"artifact_name\", \"vul_id\", \"vul_pkg_id\", \"vul_pkg_name\", \"vul_installed_version\", \"vul_fixed_version\", \"vul_title\", \"vul_severity\", \"vul_published_date\", \"vul_last_modified_date\" \nFROM \"default\".\"trivyimage\"\nWHERE EventTime >= toDateTime(1693581675) AND EventTime <= toDateTime(1694186475)\nORDER BY EventTime DESC", "refId": "A", "round": "0s", "skip_comments": true From 4a53a3032b6a97091618a8b63ff1cb4dd1ac1a32 Mon Sep 17 00:00:00 2001 From: Akash LM Date: Thu, 18 Apr 2024 21:37:24 +0530 Subject: [PATCH 262/263] config: Update image tag to v1.1.7 --- charts/agent/Chart.yaml | 4 ++-- charts/agent/values.yaml | 6 +++--- charts/client/Chart.yaml | 4 ++-- charts/client/values.yaml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/charts/agent/Chart.yaml b/charts/agent/Chart.yaml index 33444035..deb5a7fe 100644 --- a/charts/agent/Chart.yaml +++ b/charts/agent/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.20 +version: 1.1.21 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.6" +appVersion: "v1.1.7" dependencies: - name: kuberhealthy condition: kuberhealthy.enabled diff --git a/charts/agent/values.yaml b/charts/agent/values.yaml index 5ac670e1..08701e04 100644 --- a/charts/agent/values.yaml +++ b/charts/agent/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/kubviz-agent pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.6" + tag: "v1.1.7" imagePullSecrets: [] nameOverride: "" @@ -50,7 +50,7 @@ git_bridge: image: repository: ghcr.io/intelops/kubviz/git-agent pullPolicy: Always - tag: "v1.1.6" + tag: "v1.1.7" resources: limits: cpu: 200m @@ -92,7 +92,7 @@ container_bridge: image: repository: ghcr.io/intelops/kubviz/container-agent pullPolicy: Always - tag: "v1.1.6" + tag: "v1.1.7" resources: limits: cpu: 200m diff --git a/charts/client/Chart.yaml b/charts/client/Chart.yaml index c88c585c..024cd3bb 100644 --- a/charts/client/Chart.yaml +++ b/charts/client/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.25 +version: 1.1.26 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v1.1.6" +appVersion: "v1.1.7" dependencies: - name: nats condition: nats.enabled diff --git a/charts/client/values.yaml b/charts/client/values.yaml index 0e418226..4fe08c21 100644 --- a/charts/client/values.yaml +++ b/charts/client/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/intelops/kubviz/client pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "v1.1.6" + tag: "v1.1.7" imagePullSecrets: [] nameOverride: "" @@ -164,7 +164,7 @@ migration: image: repository: ghcr.io/intelops/kubviz/migration pullPolicy: Always - tag: "v1.1.6" + tag: "v1.1.7" schema: path: "/sql" From bdec163a616700ea43cf317d537a1f1a32901d0b Mon Sep 17 00:00:00 2001 From: an1l4 <1995anila@gmail.com> Date: Fri, 3 May 2024 09:55:55 +0530 Subject: [PATCH 263/263] graphql docker files --- steps-to-test.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/steps-to-test.txt b/steps-to-test.txt index f34bdfdd..04d60b68 100644 --- a/steps-to-test.txt +++ b/steps-to-test.txt @@ -162,3 +162,5 @@ query { getall_resources DeletedAPIs - by cluster name depricated - by cluster name + +graphql and other docker files included data \ No newline at end of file