diff options
author | Douwe Maan <douwe@selenight.nl> | 2017-03-07 16:39:52 -0600 |
---|---|---|
committer | Douwe Maan <douwe@selenight.nl> | 2017-03-07 16:39:52 -0600 |
commit | 3420c6cc4ef53d78db276bde06dbd9ab47165fb3 (patch) | |
tree | 2e34fd77be6424157243b67911f5c53d3a585692 /app/models | |
parent | 9895d6707d51140b3cc75e925cfd775c6bd93f83 (diff) | |
parent | 7f2819b778b055278a7fafe9c782d12d09dbd2ea (diff) | |
download | gitlab-ce-3420c6cc4ef53d78db276bde06dbd9ab47165fb3.tar.gz |
Merge branch 'master' into orderable-issues
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/chat_team.rb | 1 | ||||
-rw-r--r-- | app/models/ci/build.rb | 58 | ||||
-rw-r--r-- | app/models/ci/trigger.rb | 8 | ||||
-rw-r--r-- | app/models/concerns/has_status.rb | 2 | ||||
-rw-r--r-- | app/models/environment.rb | 8 | ||||
-rw-r--r-- | app/models/oauth_access_grant.rb | 4 | ||||
-rw-r--r-- | app/models/oauth_access_token.rb | 2 | ||||
-rw-r--r-- | app/models/personal_access_token.rb | 26 | ||||
-rw-r--r-- | app/models/project.rb | 11 | ||||
-rw-r--r-- | app/models/project_services/monitoring_service.rb | 16 | ||||
-rw-r--r-- | app/models/project_services/prometheus_service.rb | 93 | ||||
-rw-r--r-- | app/models/repository.rb | 4 | ||||
-rw-r--r-- | app/models/service.rb | 1 | ||||
-rw-r--r-- | app/models/user.rb | 3 |
14 files changed, 213 insertions, 24 deletions
diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb index 7952141a0d6..c52b6f15913 100644 --- a/app/models/chat_team.rb +++ b/app/models/chat_team.rb @@ -1,5 +1,6 @@ class ChatTeam < ActiveRecord::Base validates :team_id, presence: true + validates :namespace, uniqueness: true belongs_to :namespace end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index d69643967a1..3722047251d 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -517,6 +517,27 @@ module Ci ] end + def steps + [Gitlab::Ci::Build::Step.from_commands(self), + Gitlab::Ci::Build::Step.from_after_script(self)].compact + end + + def image + Gitlab::Ci::Build::Image.from_image(self) + end + + def services + Gitlab::Ci::Build::Image.from_services(self) + end + + def artifacts + [options[:artifacts]] + end + + def cache + [options[:cache]] + end + def credentials Gitlab::Ci::Build::Credentials::Factory.new(self).create! end @@ -543,10 +564,35 @@ module Ci @unscoped_project ||= Project.unscoped.find_by(id: gl_project_id) end + CI_REGISTRY_USER = 'gitlab-ci-token'.freeze + def predefined_variables variables = [ { key: 'CI', value: 'true', public: true }, { key: 'GITLAB_CI', value: 'true', public: true }, + { key: 'CI_SERVER_NAME', value: 'GitLab', public: true }, + { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true }, + { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }, + { key: 'CI_JOB_ID', value: id.to_s, public: true }, + { key: 'CI_JOB_NAME', value: name, public: true }, + { key: 'CI_JOB_STAGE', value: stage, public: true }, + { key: 'CI_JOB_TOKEN', value: token, public: false }, + { key: 'CI_COMMIT_SHA', value: sha, public: true }, + { key: 'CI_COMMIT_REF_NAME', value: ref, public: true }, + { key: 'CI_COMMIT_REF_SLUG', value: ref_slug, public: true }, + { key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER, public: true }, + { key: 'CI_REGISTRY_PASSWORD', value: token, public: false }, + { key: 'CI_REPOSITORY_URL', value: repo_url, public: false } + ] + + variables << { key: "CI_COMMIT_TAG", value: ref, public: true } if tag? + variables << { key: "CI_PIPELINE_TRIGGERED", value: 'true', public: true } if trigger_request + variables << { key: "CI_JOB_MANUAL", value: 'true', public: true } if action? + variables.concat(legacy_variables) + end + + def legacy_variables + variables = [ { key: 'CI_BUILD_ID', value: id.to_s, public: true }, { key: 'CI_BUILD_TOKEN', value: token, public: false }, { key: 'CI_BUILD_REF', value: sha, public: true }, @@ -554,14 +600,12 @@ module Ci { key: 'CI_BUILD_REF_NAME', value: ref, public: true }, { key: 'CI_BUILD_REF_SLUG', value: ref_slug, public: true }, { key: 'CI_BUILD_NAME', value: name, public: true }, - { key: 'CI_BUILD_STAGE', value: stage, public: true }, - { key: 'CI_SERVER_NAME', value: 'GitLab', public: true }, - { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true }, - { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true } + { key: 'CI_BUILD_STAGE', value: stage, public: true } ] - variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag? - variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request - variables << { key: 'CI_BUILD_MANUAL', value: 'true', public: true } if action? + + variables << { key: "CI_BUILD_TAG", value: ref, public: true } if tag? + variables << { key: "CI_BUILD_TRIGGERED", value: 'true', public: true } if trigger_request + variables << { key: "CI_BUILD_MANUAL", value: 'true', public: true } if action? variables end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 8aa45b2f02e..90473d41c04 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -29,8 +29,12 @@ module Ci token[0...4] end - def can_show_token?(user) - owner.blank? || owner == user + def legacy? + self.owner_id.blank? + end + + def can_access_project? + self.owner_id.blank? || Ability.allowed?(self.owner, :create_build, project) end end end diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index b819947c9e6..5101cc7e687 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -7,7 +7,7 @@ module HasStatus STARTED_STATUSES = %w[running success failed skipped manual].freeze ACTIVE_STATUSES = %w[pending running].freeze COMPLETED_STATUSES = %w[success failed canceled skipped].freeze - ORDERED_STATUSES = %w[manual failed pending running canceled success skipped].freeze + ORDERED_STATUSES = %w[failed pending running manual canceled success skipped created].freeze class_methods do def status_sql diff --git a/app/models/environment.rb b/app/models/environment.rb index 1a21b5e52b5..bf33010fd21 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -145,6 +145,14 @@ class Environment < ActiveRecord::Base project.deployment_service.terminals(self) if has_terminals? end + def has_metrics? + project.monitoring_service.present? && available? && last_deployment.present? + end + + def metrics + project.monitoring_service.metrics(self) if has_metrics? + end + # An environment name is not necessarily suitable for use in URLs, DNS # or other third-party contexts, so provide a slugified version. A slug has # the following properties: diff --git a/app/models/oauth_access_grant.rb b/app/models/oauth_access_grant.rb new file mode 100644 index 00000000000..3a997406565 --- /dev/null +++ b/app/models/oauth_access_grant.rb @@ -0,0 +1,4 @@ +class OauthAccessGrant < Doorkeeper::AccessGrant + belongs_to :resource_owner, class_name: 'User' + belongs_to :application, class_name: 'Doorkeeper::Application' +end diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb index 116fb71ac08..b85f5dbaf2e 100644 --- a/app/models/oauth_access_token.rb +++ b/app/models/oauth_access_token.rb @@ -1,4 +1,4 @@ -class OauthAccessToken < ActiveRecord::Base +class OauthAccessToken < Doorkeeper::AccessToken belongs_to :resource_owner, class_name: 'User' belongs_to :application, class_name: 'Doorkeeper::Application' end diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 10a34c42fd8..e8b000ddad6 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -1,4 +1,5 @@ class PersonalAccessToken < ActiveRecord::Base + include Expirable include TokenAuthenticatable add_authentication_token_field :token @@ -6,17 +7,30 @@ class PersonalAccessToken < ActiveRecord::Base belongs_to :user - scope :active, -> { where(revoked: false).where("expires_at >= NOW() OR expires_at IS NULL") } + before_save :ensure_token + + scope :active, -> { where("revoked = false AND (expires_at >= NOW() OR expires_at IS NULL)") } scope :inactive, -> { where("revoked = true OR expires_at < NOW()") } + scope :with_impersonation, -> { where(impersonation: true) } + scope :without_impersonation, -> { where(impersonation: false) } - def self.generate(params) - personal_access_token = self.new(params) - personal_access_token.ensure_token - personal_access_token - end + validates :scopes, presence: true + validate :validate_api_scopes def revoke! self.revoked = true self.save end + + def active? + !revoked? && !expired? + end + + protected + + def validate_api_scopes + unless scopes.all? { |scope| Gitlab::Auth::API_SCOPES.include?(scope.to_sym) } + errors.add :scopes, "can only contain API scopes" + end + end end diff --git a/app/models/project.rb b/app/models/project.rb index 7d211784c3c..8c2dadf4659 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -113,6 +113,7 @@ class Project < ActiveRecord::Base has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :external_wiki_service, dependent: :destroy has_one :kubernetes_service, dependent: :destroy, inverse_of: :project + has_one :prometheus_service, dependent: :destroy, inverse_of: :project has_one :mock_ci_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" @@ -392,7 +393,7 @@ class Project < ActiveRecord::Base end def repository_storage_path - Gitlab.config.repositories.storages[repository_storage] + Gitlab.config.repositories.storages[repository_storage]['path'] end def team @@ -771,6 +772,14 @@ class Project < ActiveRecord::Base @deployment_service ||= deployment_services.reorder(nil).find_by(active: true) end + def monitoring_services + services.where(category: :monitoring) + end + + def monitoring_service + @monitoring_service ||= monitoring_services.reorder(nil).find_by(active: true) + end + def jira_tracker? issues_tracker.to_param == 'jira' end diff --git a/app/models/project_services/monitoring_service.rb b/app/models/project_services/monitoring_service.rb new file mode 100644 index 00000000000..ea585721e8f --- /dev/null +++ b/app/models/project_services/monitoring_service.rb @@ -0,0 +1,16 @@ +# Base class for monitoring services +# +# These services integrate with a deployment solution like Prometheus +# to provide additional features for environments. +class MonitoringService < Service + default_value_for :category, 'monitoring' + + def self.supported_events + %w() + end + + # Environments have a number of metrics + def metrics(environment) + raise NotImplementedError + end +end diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb new file mode 100644 index 00000000000..375966b9efc --- /dev/null +++ b/app/models/project_services/prometheus_service.rb @@ -0,0 +1,93 @@ +class PrometheusService < MonitoringService + include ReactiveCaching + + self.reactive_cache_key = ->(service) { [service.class.model_name.singular, service.project_id] } + self.reactive_cache_lease_timeout = 30.seconds + self.reactive_cache_refresh_interval = 30.seconds + self.reactive_cache_lifetime = 1.minute + + # Access to prometheus is directly through the API + prop_accessor :api_url + + with_options presence: true, if: :activated? do + validates :api_url, url: true + end + + after_save :clear_reactive_cache! + + def initialize_properties + if properties.nil? + self.properties = {} + end + end + + def title + 'Prometheus' + end + + def description + 'Prometheus monitoring' + end + + def help + 'Retrieves `container_cpu_usage_seconds_total` and `container_memory_usage_bytes` from the configured Prometheus server. An `environment` label is required on each metric to identify the Environment.' + end + + def self.to_param + 'prometheus' + end + + def fields + [ + { + type: 'text', + name: 'api_url', + title: 'API URL', + placeholder: 'Prometheus API Base URL, like http://prometheus.example.com/' + } + ] + end + + # Check we can connect to the Prometheus API + def test(*args) + client.ping + + { success: true, result: 'Checked API endpoint' } + rescue Gitlab::PrometheusError => err + { success: false, result: err } + end + + def metrics(environment) + with_reactive_cache(environment.slug) do |data| + data + end + end + + # Cache metrics for specific environment + def calculate_reactive_cache(environment_slug) + return unless active? && project && !project.pending_delete? + + memory_query = %{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024} + cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))} + + { + success: true, + metrics: { + # Memory used in MB + memory_values: client.query_range(memory_query, start: 8.hours.ago), + memory_current: client.query(memory_query), + # CPU Usage rate in cores. + cpu_values: client.query_range(cpu_query, start: 8.hours.ago), + cpu_current: client.query(cpu_query) + }, + last_update: Time.now.utc + } + + rescue Gitlab::PrometheusError => err + { success: false, result: err.message } + end + + def client + @prometheus ||= Gitlab::Prometheus.new(api_url: api_url) + end +end diff --git a/app/models/repository.rb b/app/models/repository.rb index e7cc8d6e083..2a12b36a84d 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -50,10 +50,6 @@ class Repository end end - def self.storages - Gitlab.config.repositories.storages - end - def initialize(path_with_namespace, project) @path_with_namespace = path_with_namespace @project = project diff --git a/app/models/service.rb b/app/models/service.rb index 3ef4cbead10..2f75a2e4e7f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -232,6 +232,7 @@ class Service < ActiveRecord::Base mattermost pipelines_email pivotaltracker + prometheus pushover redmine slack_slash_commands diff --git a/app/models/user.rb b/app/models/user.rb index bd57904a2cd..76fb4cd470e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -325,8 +325,7 @@ class User < ActiveRecord::Base end def find_by_personal_access_token(token_string) - personal_access_token = PersonalAccessToken.active.find_by_token(token_string) if token_string - personal_access_token&.user + PersonalAccessTokensFinder.new(state: 'active').find_by(token: token_string)&.user end # Returns a user for the given SSH key. |