diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-21 12:06:14 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-21 12:06:14 +0000 |
commit | 664c4c7b49c6056136299817eb79e9f1de83e567 (patch) | |
tree | ff9e4f53481d42284f82664722278e05f5bbbcbd /spec | |
parent | 6791eefead979110cc773720daee6e58c56483d9 (diff) | |
download | gitlab-ce-664c4c7b49c6056136299817eb79e9f1de83e567.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/factories/grafana_integrations.rb | 2 | ||||
-rw-r--r-- | spec/features/admin/admin_settings_spec.rb | 31 | ||||
-rw-r--r-- | spec/fixtures/grafana/dashboard_response.json | 764 | ||||
-rw-r--r-- | spec/fixtures/grafana/datasource_response.json | 21 | ||||
-rw-r--r-- | spec/fixtures/grafana/expected_grafana_embed.json | 27 | ||||
-rw-r--r-- | spec/fixtures/grafana/simplified_dashboard_response.json | 40 | ||||
-rw-r--r-- | spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json | 1 | ||||
-rw-r--r-- | spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json | 1 | ||||
-rw-r--r-- | spec/lib/gitlab/metrics/dashboard/processor_spec.rb | 8 | ||||
-rw-r--r-- | spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb | 106 | ||||
-rw-r--r-- | spec/lib/grafana/client_spec.rb | 26 | ||||
-rw-r--r-- | spec/requests/api/branches_spec.rb | 2 | ||||
-rw-r--r-- | spec/services/create_branch_service_spec.rb | 15 | ||||
-rw-r--r-- | spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb | 177 | ||||
-rw-r--r-- | spec/support/helpers/grafana_api_helpers.rb | 32 | ||||
-rw-r--r-- | spec/support/helpers/login_helpers.rb | 2 |
16 files changed, 1247 insertions, 8 deletions
diff --git a/spec/factories/grafana_integrations.rb b/spec/factories/grafana_integrations.rb index c19417f5a90..4eb3bee8b28 100644 --- a/spec/factories/grafana_integrations.rb +++ b/spec/factories/grafana_integrations.rb @@ -3,7 +3,7 @@ FactoryBot.define do factory :grafana_integration, class: GrafanaIntegration do project - grafana_url { 'https://grafana.com' } + grafana_url { 'https://grafana.example.com' } token { SecureRandom.hex(10) } end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index e1c9364067a..99a6165cfc9 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do include StubENV include TermsHelper + include MobileHelpers let(:admin) { create(:admin) } @@ -450,6 +451,32 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc expect(page).to have_link(text: 'Support', href: new_support_url) end end + + it 'Shows admin dashboard links on bigger screen' do + visit root_dashboard_path + + page.within '.navbar' do + expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true) + expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) + end + end + + it 'Relocates admin dashboard links to dropdown list on smaller screen', :js do + resize_screen_xs + visit root_dashboard_path + + page.within '.navbar' do + expect(page).not_to have_link(text: 'Admin Area', href: admin_root_path, visible: true) + expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) + end + + find('.header-more').click + + page.within '.navbar' do + expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true) + expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true) + end + end end context 'when in admin_mode' do @@ -462,7 +489,7 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc it 'can leave admin mode' do page.within('.navbar-sub-nav') do # Select first, link is also included in mobile view list - click_on 'Leave admin mode', match: :first + click_on 'Leave Admin Mode', match: :first expect(page).to have_link(href: new_admin_session_path) end @@ -481,7 +508,7 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc before do page.within('.navbar-sub-nav') do # Select first, link is also included in mobile view list - click_on 'Leave admin mode', match: :first + click_on 'Leave Admin Mode', match: :first end end diff --git a/spec/fixtures/grafana/dashboard_response.json b/spec/fixtures/grafana/dashboard_response.json new file mode 100644 index 00000000000..4743ec39b44 --- /dev/null +++ b/spec/fixtures/grafana/dashboard_response.json @@ -0,0 +1,764 @@ +{ + "meta": { + "type": "db", + "canSave": true, + "canEdit": true, + "canAdmin": true, + "canStar": true, + "slug": "gitlab-omnibus-redis", + "url": "/-/grafana/d/XDaNK6amz/gitlab-omnibus-redis", + "expires": "0001-01-01T00:00:00Z", + "created": "2019-10-04T13:43:20Z", + "updated": "2019-10-04T13:43:20Z", + "updatedBy": "Anonymous", + "createdBy": "Anonymous", + "version": 1, + "hasAcl": false, + "isFolder": false, + "folderId": 1, + "folderTitle": "GitLab Omnibus", + "folderUrl": "/-/grafana/dashboards/f/l2EpNh2Zk/gitlab-omnibus", + "provisioned": true, + "provisionedExternalId": "redis.json" + }, + "dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "description": "GitLab Omnibus dashboard for Redis servers", + "editable": true, + "gnetId": 763, + "graphTooltip": 0, + "id": 3, + "iteration": 1556027798221, + "links": [], + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + "datasource": "GitLab Omnibus", + "decimals": 0, + "editable": true, + "error": false, + "format": "dtdurations", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { "h": 3, "w": 4, "x": 0, "y": 0 }, + "id": 9, + "interval": null, + "isNew": true, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { "name": "value to text", "value": 1 }, + { "name": "range to text", "value": 2 } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [{ "from": "null", "text": "N/A", "to": "null" }], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "addr", + "targets": [ + { + "expr": "avg(time() - redis_start_time_seconds{instance=~\"$instance\"})", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 1800 + } + ], + "thresholds": "", + "title": "Uptime", + "type": "singlestat", + "valueFontSize": "70%", + "valueMaps": [{ "op": "=", "text": "N/A", "value": "null" }], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + "datasource": "GitLab Omnibus", + "decimals": 0, + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { "h": 3, "w": 4, "x": 4, "y": 0 }, + "hideTimeOverride": true, + "id": 12, + "interval": null, + "isNew": true, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { "name": "value to text", "value": 1 }, + { "name": "range to text", "value": 2 } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [{ "from": "null", "text": "N/A", "to": "null" }], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(\n avg_over_time(redis_connected_clients{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 2 + } + ], + "thresholds": "", + "timeFrom": "1m", + "timeShift": null, + "title": "Clients", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [{ "op": "=", "text": "N/A", "value": "null" }], + "valueName": "avg" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { "h": 6, "w": 8, "x": 8, "y": 0 }, + "id": 2, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(\n rate(redis_commands_processed_total{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "", + "metric": "A", + "refId": "A", + "step": 240, + "target": "" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Commands Executed", + "tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "cumulative" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "reqps", "label": null, "logBase": 1, "max": null, "min": "0", "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "decimals": 2, + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { "h": 6, "w": 8, "x": 16, "y": 0 }, + "id": 1, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": true, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(\n rate(redis_keyspace_hits_total{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "hide": false, + "interval": "1m", + "intervalFactor": 1, + "legendFormat": "hits", + "metric": "", + "refId": "A", + "step": 240, + "target": "" + }, + { + "expr": "sum(\n rate(redis_keyspace_misses_total{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "hide": false, + "interval": "1m", + "intervalFactor": 1, + "legendFormat": "misses", + "metric": "", + "refId": "B", + "step": 240, + "target": "" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Hits, Misses per Second", + "tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "individual" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "short", "label": "", "logBase": 1, "max": null, "min": 0, "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": { "max": "#BF1B00" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { "h": 10, "w": 8, "x": 0, "y": 3 }, + "id": 7, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null as zero", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [{ "alias": "/max - .*/", "dashes": true }], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "redis_memory_used_bytes{instance=~\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used - {{instance}}", + "metric": "", + "refId": "A", + "step": 240, + "target": "" + }, + { + "expr": "redis_config_maxmemory{instance=~\"$instance\"} \u003e 0", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "max - {{instance}}", + "refId": "B", + "step": 240 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "cumulative" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "bytes", "label": null, "logBase": 1, "max": null, "min": 0, "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": { + "evicts": "#890F02", + "memcached_items_evicted_total{instance=\"172.17.0.1:9150\",job=\"prometheus\"}": "#890F02", + "reclaims": "#3F6833" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { "h": 7, "w": 8, "x": 8, "y": 6 }, + "id": 8, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [{ "alias": "reclaims", "yaxis": 2 }], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(redis_expired_keys_total{instance=~\"$instance\"}[$__interval]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "expired", + "metric": "", + "refId": "A", + "step": 240, + "target": "" + }, + { + "expr": "sum(rate(redis_evicted_keys_total{instance=~\"$instance\"}[$__interval]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "evicted", + "refId": "B", + "step": 240 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Expired / Evicted", + "tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "cumulative" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "short", "label": null, "logBase": 1, "max": null, "min": "0", "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 1, + "grid": {}, + "gridPos": { "h": 7, "w": 8, "x": 16, "y": 6 }, + "id": 10, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(\n rate(redis_net_input_bytes_total{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "In", + "refId": "A", + "step": 240 + }, + { + "expr": "sum(\n rate(redis_net_output_bytes_total{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Out", + "refId": "B", + "step": 240 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Network I/O", + "tooltip": { "msResolution": true, "shared": true, "sort": 0, "value_type": "cumulative" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "Bps", "label": null, "logBase": 1, "max": null, "min": "0", "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 8, + "grid": {}, + "gridPos": { "h": 7, "w": 16, "x": 0, "y": 13 }, + "id": 14, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum without (instance) (\n rate(redis_commands_total{instance=~\"$instance\"}[$__interval])\n) \u003e 0", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{ cmd }}", + "metric": "redis_command_calls_total", + "refId": "A", + "step": 240 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Command Calls / sec", + "tooltip": { "msResolution": true, "shared": true, "sort": 2, "value_type": "individual" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "short", "label": null, "logBase": 1, "max": null, "min": "0", "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 7, + "grid": {}, + "gridPos": { "h": 7, "w": 8, "x": 16, "y": 13 }, + "id": 13, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(redis_db_keys{instance=~\"$instance\"} - redis_db_keys_expiring{instance=~\"$instance\"}) ", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "not expiring", + "refId": "A", + "step": 240, + "target": "" + }, + { + "expr": "sum(redis_db_keys_expiring{instance=~\"$instance\"})", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "expiring", + "metric": "", + "refId": "B", + "step": 240 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Expiring vs Not-Expiring Keys", + "tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "individual" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "short", "label": null, "logBase": 1, "max": null, "min": "0", "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "GitLab Omnibus", + "editable": true, + "error": false, + "fill": 7, + "grid": {}, + "gridPos": { "h": 7, "w": 16, "x": 0, "y": 20 }, + "id": 5, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (db) (\n redis_db_keys{instance=~\"$instance\"}\n)", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{ db }} ", + "refId": "A", + "step": 240, + "target": "" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Items per DB", + "tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "individual" }, + "type": "graph", + "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, + "yaxes": [ + { "format": "none", "label": null, "logBase": 1, "max": null, "min": "0", "show": true }, + { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } + ], + "yaxis": { "align": false, "alignLevel": null } + } + ], + "refresh": "1m", + "schemaVersion": 18, + "style": "dark", + "tags": ["redis"], + "templating": { + "list": [ + { + "allValue": null, + "current": { "tags": [], "text": "All", "value": "$__all" }, + "datasource": "GitLab Omnibus", + "definition": "", + "hide": 0, + "includeAll": true, + "label": null, + "multi": false, + "name": "instance", + "options": [], + "query": "label_values(up{job=\"redis\"}, instance)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { "from": "now-24h", "to": "now" }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "", + "title": "GitLab Omnibus - Redis", + "uid": "XDaNK6amz", + "version": 1 + } +} diff --git a/spec/fixtures/grafana/datasource_response.json b/spec/fixtures/grafana/datasource_response.json new file mode 100644 index 00000000000..07c075beb35 --- /dev/null +++ b/spec/fixtures/grafana/datasource_response.json @@ -0,0 +1,21 @@ +{ + "id": 1, + "orgId": 1, + "name": "GitLab Omnibus", + "type": "prometheus", + "typeLogoUrl": "", + "access": "proxy", + "url": "http://localhost:9090", + "password": "", + "user": "", + "database": "", + "basicAuth": false, + "basicAuthUser": "", + "basicAuthPassword": "", + "withCredentials": false, + "isDefault": true, + "jsonData": {}, + "secureJsonFields": {}, + "version": 1, + "readOnly": true +} diff --git a/spec/fixtures/grafana/expected_grafana_embed.json b/spec/fixtures/grafana/expected_grafana_embed.json new file mode 100644 index 00000000000..72fb5477b9e --- /dev/null +++ b/spec/fixtures/grafana/expected_grafana_embed.json @@ -0,0 +1,27 @@ +{ + "panel_groups": [ + { + "panels": [ + { + "title": "Network I/O", + "type": "area-chart", + "y_label": "", + "metrics": [ + { + "id": "In_0", + "query_range": "sum( rate(redis_net_input_bytes_total{instance=~\"localhost:9121\"}[1m]))", + "label": "In", + "prometheus_endpoint_path": "/foo/bar/-/grafana/proxy/1/api/v1/query_range?query=sum%28++rate%28redis_net_input_bytes_total%7Binstance%3D~%22localhost%3A9121%22%7D%5B1m%5D%29%29" + }, + { + "id": "Out_1", + "query_range": "sum( rate(redis_net_output_bytes_total{instance=~\"localhost:9121\"}[1m]))", + "label": "Out", + "prometheus_endpoint_path": "/foo/bar/-/grafana/proxy/1/api/v1/query_range?query=sum%28++rate%28redis_net_output_bytes_total%7Binstance%3D~%22localhost%3A9121%22%7D%5B1m%5D%29%29" + } + ] + } + ] + } + ] +} diff --git a/spec/fixtures/grafana/simplified_dashboard_response.json b/spec/fixtures/grafana/simplified_dashboard_response.json new file mode 100644 index 00000000000..b450fda082b --- /dev/null +++ b/spec/fixtures/grafana/simplified_dashboard_response.json @@ -0,0 +1,40 @@ +{ + "dashboard": { + "panels": [ + { + "datasource": "GitLab Omnibus", + "id": 8, + "lines": true, + "targets": [ + { + "expr": "sum(\n rate(redis_net_input_bytes_total{instance=~\"$instance\"}[$__interval])\n)", + "format": "time_series", + "interval": "1m", + "legendFormat": "In", + "refId": "A" + }, + { + "expr": "sum(\n rate(redis_net_output_bytes_total{instance=~\"[[instance]]\"}[$__interval])\n)", + "format": "time_series", + "interval": "1m", + "legendFormat": "Out", + "refId": "B" + } + ], + "title": "Network I/O", + "type": "graph", + "yaxes": [{ "format": "Bps" }, { "format": "short" }] + } + ], + "templating": { + "list": [ + { + "current": { + "value": "localhost:9121" + }, + "name": "instance" + } + ] + } + } +} diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json index 9c1be32645a..ac40f2dcd13 100644 --- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json @@ -1,7 +1,6 @@ { "type": "object", "required": [ - "unit", "label", "prometheus_endpoint_path" ], diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json index 1548daacd64..a16f1ef592f 100644 --- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json @@ -3,7 +3,6 @@ "required": [ "title", "y_label", - "weight", "metrics" ], "properties": { diff --git a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb index e2ce1869810..4fa136bc405 100644 --- a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb @@ -25,6 +25,14 @@ describe Gitlab::Metrics::Dashboard::Processor do end end + context 'when the dashboard is not present' do + let(:dashboard_yml) { nil } + + it 'returns nil' do + expect(dashboard).to be_nil + end + end + context 'when dashboard config corresponds to common metrics' do let!(:common_metric) { create(:prometheus_metric, :common, identifier: 'metric_a1') } diff --git a/spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb b/spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb new file mode 100644 index 00000000000..5c2ec6dae6b --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter do + include GrafanaApiHelpers + + let_it_be(:namespace) { create(:namespace, name: 'foo') } + let_it_be(:project) { create(:project, namespace: namespace, name: 'bar') } + + describe '#transform!' do + let(:grafana_dashboard) { JSON.parse(fixture_file('grafana/simplified_dashboard_response.json'), symbolize_names: true) } + let(:datasource) { JSON.parse(fixture_file('grafana/datasource_response.json'), symbolize_names: true) } + + let(:dashboard) { described_class.new(project, {}, params).transform! } + + let(:params) do + { + grafana_dashboard: grafana_dashboard, + datasource: datasource, + grafana_url: valid_grafana_dashboard_link('https://grafana.example.com') + } + end + + context 'when the query and resources are configured correctly' do + let(:expected_dashboard) { JSON.parse(fixture_file('grafana/expected_grafana_embed.json'), symbolize_names: true) } + + it 'generates a gitlab-yml formatted dashboard' do + expect(dashboard).to eq(expected_dashboard) + end + end + + context 'when the inputs are invalid' do + shared_examples_for 'processing error' do + it 'raises a processing error' do + expect { dashboard } + .to raise_error(Gitlab::Metrics::Dashboard::Stages::InputFormatValidator::DashboardProcessingError) + end + end + + context 'when the datasource is not proxyable' do + before do + params[:datasource][:access] = 'not-proxy' + end + + it_behaves_like 'processing error' + end + + context 'when query param "panelId" is not specified' do + before do + params[:grafana_url].gsub!('panelId=8', '') + end + + it_behaves_like 'processing error' + end + + context 'when query param "from" is not specified' do + before do + params[:grafana_url].gsub!('from=1570397739557', '') + end + + it_behaves_like 'processing error' + end + + context 'when query param "to" is not specified' do + before do + params[:grafana_url].gsub!('to=1570484139557', '') + end + + it_behaves_like 'processing error' + end + + context 'when the panel is not a graph' do + before do + params[:grafana_dashboard][:dashboard][:panels][0][:type] = 'singlestat' + end + + it_behaves_like 'processing error' + end + + context 'when the panel is not a line graph' do + before do + params[:grafana_dashboard][:dashboard][:panels][0][:lines] = false + end + + it_behaves_like 'processing error' + end + + context 'when the query dashboard includes undefined variables' do + before do + params[:grafana_url].gsub!('&var-instance=localhost:9121', '') + end + + it_behaves_like 'processing error' + end + + context 'when the expression contains unsupported global variables' do + before do + params[:grafana_dashboard][:dashboard][:panels][0][:targets][0][:expr] = 'sum(important_metric[$__interval_ms])' + end + + it_behaves_like 'processing error' + end + end + end +end diff --git a/spec/lib/grafana/client_spec.rb b/spec/lib/grafana/client_spec.rb index bd93a3c59a2..699344e940e 100644 --- a/spec/lib/grafana/client_spec.rb +++ b/spec/lib/grafana/client_spec.rb @@ -35,7 +35,7 @@ describe Grafana::Client do it 'does not follow redirects' do expect { subject }.to raise_exception( Grafana::Client::Error, - 'Grafana response status code: 302' + 'Grafana response status code: 302, Message: {}' ) expect(redirect_req_stub).to have_been_requested @@ -67,6 +67,30 @@ describe Grafana::Client do end end + describe '#get_dashboard' do + let(:grafana_api_url) { 'https://grafanatest.com/-/grafana-project/api/dashboards/uid/FndfgnX' } + + subject do + client.get_dashboard(uid: 'FndfgnX') + end + + it_behaves_like 'calls grafana api' + it_behaves_like 'no redirects' + it_behaves_like 'handles exceptions' + end + + describe '#get_datasource' do + let(:grafana_api_url) { 'https://grafanatest.com/-/grafana-project/api/datasources/name/Test%20Name' } + + subject do + client.get_datasource(name: 'Test Name') + end + + it_behaves_like 'calls grafana api' + it_behaves_like 'no redirects' + it_behaves_like 'handles exceptions' + end + describe '#proxy_datasource' do let(:grafana_api_url) do 'https://grafanatest.com/-/grafana-project/' \ diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index f9c8b42afa8..d1e20cb1770 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -602,7 +602,7 @@ describe API::Branches do post api(route, user), params: { branch: 'new_design3', ref: 'foo' } expect(response).to have_gitlab_http_status(400) - expect(json_response['message']).to eq('Invalid reference name') + expect(json_response['message']).to eq('Invalid reference name: new_design3') end end diff --git a/spec/services/create_branch_service_spec.rb b/spec/services/create_branch_service_spec.rb index 0d34c7f9a82..9661173c9e7 100644 --- a/spec/services/create_branch_service_spec.rb +++ b/spec/services/create_branch_service_spec.rb @@ -22,5 +22,20 @@ describe CreateBranchService do expect(project.repository.branch_exists?('my-feature')).to be_truthy end end + + context 'when creating a branch fails' do + let(:project) { create(:project_empty_repo) } + + before do + allow(project.repository).to receive(:add_branch).and_return(false) + end + + it 'retruns an error with the branch name' do + result = service.execute('my-feature', 'master') + + expect(result[:status]).to eq(:error) + expect(result[:message]).to eq("Invalid reference name: my-feature") + end + end end end diff --git a/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb b/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb new file mode 100644 index 00000000000..f200c636aac --- /dev/null +++ b/spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Metrics::Dashboard::GrafanaMetricEmbedService do + include MetricsDashboardHelpers + include ReactiveCachingHelpers + include GrafanaApiHelpers + + let_it_be(:project) { build(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:grafana_integration) { create(:grafana_integration, project: project) } + + let(:grafana_url) do + valid_grafana_dashboard_link(grafana_integration.grafana_url) + end + + before do + project.add_maintainer(user) + end + + describe '.valid_params?' do + let(:valid_params) { { embedded: true, grafana_url: grafana_url } } + + subject { described_class.valid_params?(params) } + + let(:params) { valid_params } + + it { is_expected.to be_truthy } + + context 'not embedded' do + let(:params) { valid_params.except(:embedded) } + + it { is_expected.to be_falsey } + end + + context 'undefined grafana_url' do + let(:params) { valid_params.except(:grafana_url) } + + it { is_expected.to be_falsey } + end + end + + describe '.from_cache' do + let(:params) { [project.id, user.id, grafana_url] } + + subject { described_class.from_cache(*params) } + + it 'initializes an instance of GrafanaMetricEmbedService' do + expect(subject).to be_an_instance_of(described_class) + expect(subject.project).to eq(project) + expect(subject.current_user).to eq(user) + expect(subject.params[:grafana_url]).to eq(grafana_url) + end + end + + describe '#get_dashboard', :use_clean_rails_memory_store_caching do + let(:service_params) do + [ + project, + user, + { + embedded: true, + grafana_url: grafana_url + } + ] + end + + let(:service) { described_class.new(*service_params) } + let(:service_call) { service.get_dashboard } + + context 'without caching' do + before do + synchronous_reactive_cache(service) + end + + it_behaves_like 'raises error for users with insufficient permissions' + + context 'without a grafana integration' do + before do + allow(project).to receive(:grafana_integration).and_return(nil) + end + + it_behaves_like 'misconfigured dashboard service response', :bad_request + end + + context 'when grafana cannot be reached' do + before do + allow(grafana_integration.client).to receive(:get_dashboard).and_raise(::Grafana::Client::Error) + end + + it_behaves_like 'misconfigured dashboard service response', :service_unavailable + end + + context 'when panelId is missing' do + let(:grafana_url) do + grafana_integration.grafana_url + + '/d/XDaNK6amz/gitlab-omnibus-redis' \ + '?from=1570397739557&to=1570484139557' + end + + before do + stub_dashboard_request(grafana_integration.grafana_url) + end + + it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity + end + + context 'when uid is missing' do + let(:grafana_url) { grafana_integration.grafana_url + '/d/' } + + before do + stub_dashboard_request(grafana_integration.grafana_url) + end + + it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity + end + + context 'when the dashboard response contains misconfigured json' do + before do + stub_dashboard_request(grafana_integration.grafana_url, body: '') + end + + it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity + end + + context 'when the datasource response contains misconfigured json' do + before do + stub_dashboard_request(grafana_integration.grafana_url) + stub_datasource_request(grafana_integration.grafana_url, body: '') + end + + it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity + end + + context 'when the embed was created successfully' do + before do + stub_dashboard_request(grafana_integration.grafana_url) + stub_datasource_request(grafana_integration.grafana_url) + end + + it_behaves_like 'valid embedded dashboard service response' + end + end + + context 'with caching', :use_clean_rails_memory_store_caching do + let(:cache_params) { [project.id, user.id, grafana_url] } + + context 'when value not present in cache' do + it 'returns nil' do + expect(ReactiveCachingWorker) + .to receive(:perform_async) + .with(service.class, service.id, *cache_params) + + expect(service_call).to eq(nil) + end + end + + context 'when value present in cache' do + let(:return_value) { { 'http_status' => :ok, 'dashboard' => '{}' } } + + before do + stub_reactive_cache(service, return_value, cache_params) + end + + it 'returns cached value' do + expect(ReactiveCachingWorker) + .not_to receive(:perform_async) + .with(service.class, service.id, *cache_params) + + expect(service_call[:http_status]).to eq(return_value[:http_status]) + expect(service_call[:dashboard]).to eq(return_value[:dashboard]) + end + end + end + end +end diff --git a/spec/support/helpers/grafana_api_helpers.rb b/spec/support/helpers/grafana_api_helpers.rb new file mode 100644 index 00000000000..b212cbf2943 --- /dev/null +++ b/spec/support/helpers/grafana_api_helpers.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module GrafanaApiHelpers + def valid_grafana_dashboard_link(base_url) + base_url + + '/d/XDaNK6amz/gitlab-omnibus-redis' \ + '?from=1570397739557&to=1570484139557' \ + '&var-instance=localhost:9121&panelId=8' + end + + def stub_dashboard_request(base_url, path: '/api/dashboards/uid/XDaNK6amz', body: nil) + body ||= fixture_file('grafana/dashboard_response.json') + + stub_request(:get, "#{base_url}#{path}") + .to_return( + status: 200, + body: body, + headers: { 'Content-Type' => 'application/json' } + ) + end + + def stub_datasource_request(base_url, path: '/api/datasources/name/GitLab%20Omnibus', body: nil) + body ||= fixture_file('grafana/datasource_response.json') + + stub_request(:get, "#{base_url}#{path}") + .to_return( + status: 200, + body: body, + headers: { 'Content-Type' => 'application/json' } + ) + end +end diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 7d5896e4eeb..1d42f26ad3e 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -53,7 +53,7 @@ module LoginHelpers fill_in 'password', with: user.password - click_button 'Enter admin mode' + click_button 'Enter Admin Mode' end def gitlab_sign_in_via(provider, user, uid, saml_response = nil) |