a3f80f9f2e
PoolRepository is a relatively new model of which the counts could help to further determine the priority of new features. Also gives some insight into the number of forks customers have.
204 lines
8.1 KiB
Ruby
204 lines
8.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
class UsageData
|
|
APPROXIMATE_COUNT_MODELS = [Label, MergeRequest, Note, Todo].freeze
|
|
|
|
class << self
|
|
def data(force_refresh: false)
|
|
Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data }
|
|
end
|
|
|
|
def uncached_data
|
|
license_usage_data.merge(system_usage_data)
|
|
.merge(features_usage_data)
|
|
.merge(components_usage_data)
|
|
.merge(cycle_analytics_usage_data)
|
|
.merge(usage_counters)
|
|
end
|
|
|
|
def to_json(force_refresh: false)
|
|
data(force_refresh: force_refresh).to_json
|
|
end
|
|
|
|
def license_usage_data
|
|
usage_data = {
|
|
uuid: Gitlab::CurrentSettings.uuid,
|
|
hostname: Gitlab.config.gitlab.host,
|
|
version: Gitlab::VERSION,
|
|
installation_type: installation_type,
|
|
active_user_count: count(User.active),
|
|
recorded_at: Time.now,
|
|
edition: 'CE'
|
|
}
|
|
|
|
usage_data
|
|
end
|
|
|
|
# rubocop:disable Metrics/AbcSize
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def system_usage_data
|
|
{
|
|
counts: {
|
|
assignee_lists: count(List.assignee),
|
|
boards: count(Board),
|
|
ci_builds: count(::Ci::Build),
|
|
ci_internal_pipelines: count(::Ci::Pipeline.internal),
|
|
ci_external_pipelines: count(::Ci::Pipeline.external),
|
|
ci_pipeline_config_auto_devops: count(::Ci::Pipeline.auto_devops_source),
|
|
ci_pipeline_config_repository: count(::Ci::Pipeline.repository_source),
|
|
ci_runners: count(::Ci::Runner),
|
|
ci_triggers: count(::Ci::Trigger),
|
|
ci_pipeline_schedules: count(::Ci::PipelineSchedule),
|
|
auto_devops_enabled: count(::ProjectAutoDevops.enabled),
|
|
auto_devops_disabled: count(::ProjectAutoDevops.disabled),
|
|
deploy_keys: count(DeployKey),
|
|
deployments: count(Deployment),
|
|
successful_deployments: count(Deployment.success),
|
|
failed_deployments: count(Deployment.failed),
|
|
environments: count(::Environment),
|
|
clusters: count(::Clusters::Cluster),
|
|
clusters_enabled: count(::Clusters::Cluster.enabled),
|
|
project_clusters_enabled: count(::Clusters::Cluster.enabled.project_type),
|
|
group_clusters_enabled: count(::Clusters::Cluster.enabled.group_type),
|
|
clusters_disabled: count(::Clusters::Cluster.disabled),
|
|
project_clusters_disabled: count(::Clusters::Cluster.disabled.project_type),
|
|
group_clusters_disabled: count(::Clusters::Cluster.disabled.group_type),
|
|
clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled),
|
|
clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled),
|
|
clusters_applications_helm: count(::Clusters::Applications::Helm.available),
|
|
clusters_applications_ingress: count(::Clusters::Applications::Ingress.available),
|
|
clusters_applications_cert_managers: count(::Clusters::Applications::CertManager.available),
|
|
clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.available),
|
|
clusters_applications_runner: count(::Clusters::Applications::Runner.available),
|
|
clusters_applications_knative: count(::Clusters::Applications::Knative.available),
|
|
in_review_folder: count(::Environment.in_review_folder),
|
|
groups: count(Group),
|
|
issues: count(Issue),
|
|
keys: count(Key),
|
|
label_lists: count(List.label),
|
|
lfs_objects: count(LfsObject),
|
|
milestone_lists: count(List.milestone),
|
|
milestones: count(Milestone),
|
|
pages_domains: count(PagesDomain),
|
|
pool_repositories: count(PoolRepository),
|
|
projects: count(Project),
|
|
projects_imported_from_github: count(Project.where(import_type: 'github')),
|
|
projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)),
|
|
projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)),
|
|
protected_branches: count(ProtectedBranch),
|
|
releases: count(Release),
|
|
remote_mirrors: count(RemoteMirror),
|
|
snippets: count(Snippet),
|
|
suggestions: count(Suggestion),
|
|
todos: count(Todo),
|
|
uploads: count(Upload),
|
|
web_hooks: count(WebHook)
|
|
}
|
|
.merge(services_usage)
|
|
.merge(approximate_counts)
|
|
}.tap do |data|
|
|
if Feature.enabled?(:group_overview_security_dashboard)
|
|
data[:counts][:user_preferences] = user_preferences_usage
|
|
end
|
|
end
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
def cycle_analytics_usage_data
|
|
Gitlab::CycleAnalytics::UsageData.new.to_json
|
|
end
|
|
|
|
def features_usage_data
|
|
features_usage_data_ce
|
|
end
|
|
|
|
def features_usage_data_ce
|
|
{
|
|
container_registry_enabled: Gitlab.config.registry.enabled,
|
|
gitlab_shared_runners_enabled: Gitlab.config.gitlab_ci.shared_runners_enabled,
|
|
gravatar_enabled: Gitlab::CurrentSettings.gravatar_enabled?,
|
|
influxdb_metrics_enabled: Gitlab::Metrics.influx_metrics_enabled?,
|
|
ldap_enabled: Gitlab.config.ldap.enabled,
|
|
mattermost_enabled: Gitlab.config.mattermost.enabled,
|
|
omniauth_enabled: Gitlab::Auth.omniauth_enabled?,
|
|
prometheus_metrics_enabled: Gitlab::Metrics.prometheus_metrics_enabled?,
|
|
reply_by_email_enabled: Gitlab::IncomingEmail.enabled?,
|
|
signup_enabled: Gitlab::CurrentSettings.allow_signup?
|
|
}
|
|
end
|
|
|
|
def usage_counters
|
|
{
|
|
web_ide_commits: Gitlab::WebIdeCommitsCounter.total_count
|
|
}
|
|
end
|
|
|
|
def components_usage_data
|
|
{
|
|
gitlab_pages: { enabled: Gitlab.config.pages.enabled, version: Gitlab::Pages::VERSION },
|
|
git: { version: Gitlab::Git.version },
|
|
database: { adapter: Gitlab::Database.adapter_name, version: Gitlab::Database.version }
|
|
}
|
|
end
|
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def services_usage
|
|
types = {
|
|
SlackService: :projects_slack_notifications_active,
|
|
SlackSlashCommandsService: :projects_slack_slash_active,
|
|
PrometheusService: :projects_prometheus_active
|
|
}
|
|
|
|
results = count(Service.unscoped.where(type: types.keys, active: true).group(:type), fallback: Hash.new(-1))
|
|
types.each_with_object({}) { |(klass, key), response| response[key] = results[klass.to_s] || 0 }
|
|
.merge(jira_usage)
|
|
end
|
|
|
|
def jira_usage
|
|
# Jira Cloud does not support custom domains as per https://jira.atlassian.com/browse/CLOUD-6999
|
|
# so we can just check for subdomains of atlassian.net
|
|
services = count(
|
|
Service.unscoped.where(type: :JiraService, active: true)
|
|
.group("CASE WHEN properties LIKE '%.atlassian.net%' THEN 'cloud' ELSE 'server' END"),
|
|
fallback: Hash.new(-1)
|
|
)
|
|
|
|
{
|
|
projects_jira_server_active: services['server'] || 0,
|
|
projects_jira_cloud_active: services['cloud'] || 0,
|
|
projects_jira_active: services['server'] == -1 ? -1 : services.values.sum
|
|
}
|
|
end
|
|
|
|
def user_preferences_usage
|
|
{} # augmented in EE
|
|
end
|
|
|
|
def count(relation, fallback: -1)
|
|
relation.count
|
|
rescue ActiveRecord::StatementInvalid
|
|
fallback
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
def approximate_counts
|
|
approx_counts = Gitlab::Database::Count.approximate_counts(APPROXIMATE_COUNT_MODELS)
|
|
|
|
APPROXIMATE_COUNT_MODELS.each_with_object({}) do |model, result|
|
|
key = model.name.underscore.pluralize.to_sym
|
|
|
|
result[key] = approx_counts[model] || -1
|
|
end
|
|
end
|
|
|
|
def installation_type
|
|
if Rails.env.production?
|
|
Gitlab::INSTALLATION_TYPE
|
|
else
|
|
"gitlab-development-kit"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|