Commit 5ef38663 authored by Pawel Chojnacki's avatar Pawel Chojnacki

Create, index and delete metrics. Verify queries simple implementaiton

parent 16b03b5f
...@@ -18,8 +18,71 @@ module Projects ...@@ -18,8 +18,71 @@ module Projects
end end
end end
def validate_query
respond_to do |format|
format.json do
if query_validation_params[:query] =~ /^avg/
render json: { query_valid: true }
else
render json: { query_valid: false }
end
end
end
end
def new
@metric = project.prometheus_metrics.new
end
def index
respond_to do |format|
format.json do
metrics = project.prometheus_metrics
if metrics.any?
render json: { metrics: PrometheusMetricSerializer.new(project: project).represent(metrics) }
else
head :no_content
end
end
end
end
def create
@metric = project.prometheus_metrics.create(metrics_params)
if @metric.persisted?
redirect_to edit_project_service_path(project, project.prometheus_service),
notice: 'Metric was successfully added.'
else
head :unprocessable_entity
end
end
def update
@metric = project.prometheus_metrics.find(params[:id])
@metric.update(metrics_params)
if @metric.persisted?
redirect_to edit_project_service_path(project, project.prometheus_service),
notice: 'Metric was successfully updated.'
else
render "edit"
end
end
def edit
@metric = project.prometheus_metrics.find(params[:id])
end
private private
def query_validation_params
params.permit(:query)
end
def metrics_params
params.require(:prometheus_metric).permit(:title, :query, :y_label, :unit, :legend)
end
def require_prometheus_metrics! def require_prometheus_metrics!
render_404 unless project.prometheus_service.present? render_404 unless project.prometheus_service.present?
end end
......
class Projects::PrometheusController < Projects::ApplicationController
before_action :authorize_read_project!
before_action :require_prometheus_metrics!
def active_metrics
respond_to do |format|
format.json do
matched_metrics = project.prometheus_service.matched_metrics || {}
if matched_metrics.any?
render json: matched_metrics
else
head :no_content
end
end
end
end
private
def require_prometheus_metrics!
render_404 unless project.prometheus_service.present?
end
end
...@@ -196,6 +196,8 @@ class Project < ActiveRecord::Base ...@@ -196,6 +196,8 @@ class Project < ActiveRecord::Base
has_one :cluster_project, class_name: 'Clusters::Project' has_one :cluster_project, class_name: 'Clusters::Project'
has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster' has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster'
has_many :prometheus_metrics
# Container repositories need to remove data from the container registry, # Container repositories need to remove data from the container registry,
# which is not managed by the DB. Hence we're still using dependent: :destroy # which is not managed by the DB. Hence we're still using dependent: :destroy
# here. # here.
......
class PrometheusMetric < ActiveRecord::Base
belongs_to :project, required: true, validate: true
validates :title, presence: true
validates :query, presence: true
end
class PrometheusQuery < ActiveRecord::Base
belongs_to :project
end
class PrometheusMetricEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :title
expose :edit_path do |prometheus_metric|
edit_project_prometheus_metric_path(prometheus_metric.project, prometheus_metric)
end
end
class PrometheusMetricSerializer < BaseSerializer
entity PrometheusMetricEntity
end
.row.prepend-top-default.append-bottom-default
.col-lg-3
%h4.prepend-top-0
Metric
%p
Metric allows you measuring stuff
= succeed "." do
= link_to "Read more about queries", help_page_path("prometheus/custom_metrics")
= form_for [@project.namespace.becomes(Namespace), @project, @metric], html: { class: 'col-lg-9' } do |f|
= form_errors(@metric)
.form-group
= f.label :title, 'Title', class: 'label-light'
= f.text_field :title, required: true, class: 'form-control'
.form-group
= f.label :query, 'Query', class: 'label-light'
= f.text_field :query, required: true, class: 'form-control'
.form-group
= f.label :y_label, 'Y label', class: 'label-light'
= f.text_field :y_label, class: 'form-control'
.form-group
= f.label :unit, 'Unit', class: 'label-light'
= f.text_field :unit, class: 'form-control'
.form-group
= f.label :legend, 'Legend', class: 'label-light'
= f.text_field :legend, class: 'form-control'
.form-actions
= f.submit 'Save', class: 'btn btn-save'
= link_to 'Cancel', edit_project_service_path(@project, @project.prometheus_service), class: 'btn btn-cancel'
- breadcrumb_title @metric.title
- add_to_breadcrumbs("Settings", edit_project_path(@project))
- add_to_breadcrumbs("Prometheus", edit_project_service_path(@project, @project.prometheus_service))
- page_title @metric.title, "Prometheus Metrics"
= render 'form'
- @no_container = true
- breadcrumb_title "Metrics"
- page_title 'Metrics'
%div{ class: container_class }
%h3.page-title
New metric
%hr
= render 'form'
...@@ -73,12 +73,10 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -73,12 +73,10 @@ constraints(ProjectUrlConstrainer.new) do
resource :mattermost, only: [:new, :create] resource :mattermost, only: [:new, :create]
namespace :prometheus do namespace :prometheus do
resources :metrics, constraints: { id: /[^\/]+/ }, only: [:index, :show, :edit, :update, :destroy] do resources :metrics, constraints: { id: /[^\/]+/ }, only: [:index, :new, :create, :edit, :update, :destroy] do
post :validate_query, on: :collection post :validate_query, on: :collection
get :active, on: :collection get :active, on: :collection
end end
get :active_metrics
end end
resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create, :edit, :update] do resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create, :edit, :update] do
......
class CreatePrometheusQueries < ActiveRecord::Migration class CreatePrometheusMetrics < ActiveRecord::Migration
def change def change
create_table :prometheus_queries do |t| create_table :prometheus_metrics do |t|
t.references :project, index: true, foreign_key: true t.references :project, index: true, foreign_key: true
t.string :title t.string :title
t.string :query t.string :query
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20171213160445) do ActiveRecord::Schema.define(version: 20171214160629) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -1874,6 +1874,19 @@ ActiveRecord::Schema.define(version: 20171213160445) do ...@@ -1874,6 +1874,19 @@ ActiveRecord::Schema.define(version: 20171213160445) do
add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
create_table "prometheus_metrics", force: :cascade do |t|
t.integer "project_id"
t.string "title"
t.string "query"
t.string "y_label"
t.string "unit"
t.string "legend"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "prometheus_metrics", ["project_id"], name: "index_prometheus_metrics_on_project_id", using: :btree
create_table "protected_branch_merge_access_levels", force: :cascade do |t| create_table "protected_branch_merge_access_levels", force: :cascade do |t|
t.integer "protected_branch_id", null: false t.integer "protected_branch_id", null: false
t.integer "access_level", default: 40 t.integer "access_level", default: 40
...@@ -2542,6 +2555,7 @@ ActiveRecord::Schema.define(version: 20171213160445) do ...@@ -2542,6 +2555,7 @@ ActiveRecord::Schema.define(version: 20171213160445) do
add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade
add_foreign_key "project_mirror_data", "projects", name: "fk_d1aad367d7", on_delete: :cascade add_foreign_key "project_mirror_data", "projects", name: "fk_d1aad367d7", on_delete: :cascade
add_foreign_key "project_statistics", "projects", on_delete: :cascade add_foreign_key "project_statistics", "projects", on_delete: :cascade
add_foreign_key "prometheus_metrics", "projects"
add_foreign_key "protected_branch_merge_access_levels", "namespaces", column: "group_id", name: "fk_98f3d044fe", on_delete: :cascade add_foreign_key "protected_branch_merge_access_levels", "namespaces", column: "group_id", name: "fk_98f3d044fe", on_delete: :cascade
add_foreign_key "protected_branch_merge_access_levels", "protected_branches", name: "fk_8a3072ccb3", on_delete: :cascade add_foreign_key "protected_branch_merge_access_levels", "protected_branches", name: "fk_8a3072ccb3", on_delete: :cascade
add_foreign_key "protected_branch_merge_access_levels", "users" add_foreign_key "protected_branch_merge_access_levels", "users"
......
...@@ -2,10 +2,9 @@ module Gitlab ...@@ -2,10 +2,9 @@ module Gitlab
module Prometheus module Prometheus
class Query class Query
include ActiveModel::Model include ActiveModel::Model
include ActiveRecord::Base # include ActiveRecord::Base
attr_accessor :unit, :series_dsl, :label, :type, track: canary
attr_accessor :unit, :series_dsl, :label, :type, :track
validates :title, :required_metrics, :weight, :y_label, :queries, presence: true validates :title, :required_metrics, :weight, :y_label, :queries, presence: true
......
require('spec_helper') require 'spec_helper'
describe Projects::Prometheus::MetricsController do describe Projects::Prometheus::MetricsController do
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -14,6 +14,24 @@ describe Projects::Prometheus::MetricsController do ...@@ -14,6 +14,24 @@ describe Projects::Prometheus::MetricsController do
sign_in(user) sign_in(user)
end end
describe 'POST #validate_query' do
context 'query is valid' do
it 'confirms query is valid' do
post :validate_query, project_params(format: :json, query: 'avg(metric)')
expect(json_response).to eq("query_valid" => true)
end
end
context 'query is invalid' do
it 'confirms query is valid' do
post :validate_query, project_params(format: :json, query: 'test(metric)')
expect(json_response).to eq("query_valid" => false)
end
end
end
describe 'GET #active' do describe 'GET #active' do
context 'when prometheus metrics are enabled' do context 'when prometheus metrics are enabled' do
context 'when data is not present' do context 'when data is not present' do
...@@ -53,6 +71,29 @@ describe Projects::Prometheus::MetricsController do ...@@ -53,6 +71,29 @@ describe Projects::Prometheus::MetricsController do
end end
end end
describe 'POST #create' do
context 'metric is valid' do
let(:valid_metric) { { prometheus_metric: { title: 'title', query: 'query' } } }
it 'shows a success flash message' do
post :create, project_params(valid_metric)
expect(flash[:notice]).to include('Metric was successfully added.')
expect(response).to redirect_to(edit_namespace_project_service_path(project.namespace, project, project.prometheus_service))
end
end
context 'metric is invalid' do
let(:invalid_metric) { { prometheus_metric: { title: 'title' } } }
it 'returns an error' do
post :create, project_params(invalid_metric)
expect(response).to have_gitlab_http_status(422)
end
end
end
def project_params(opts = {}) def project_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace, project_id: project) opts.reverse_merge(namespace_id: project.namespace, project_id: project)
end end
......
FactoryGirl.define do
factory :prometheus_metric, class: PrometheusMetric do
title 'title'
query 'avg(metric)'
y_label 'y_label'
unit 'm/s'
legend 'legend'
project nil
end
end
require 'spec_helper'
describe PrometheusMetric, type: :model do
subject { build(:prometheus_metric) }
it { is_expected.to belong_to(:project) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:query) }
end
require 'rails_helper'
RSpec.describe PrometheusQuery, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment