Commit 3f61d636 authored by Adrien Kohlbecker's avatar Adrien Kohlbecker Committed by Kushal Pandya

Move logs to core (backend)

backend side
parent 57490a31
...@@ -492,6 +492,41 @@ const Api = { ...@@ -492,6 +492,41 @@ const Api = {
buildUrl(url) { buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version)); return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
}, },
/**
* Returns pods logs for an environment with an optional pod and container
*
* @param {Object} params
* @param {Object} param.environment - Environment object
* @param {string=} params.podName - Pod name, if not set the backend assumes a default one
* @param {string=} params.containerName - Container name, if not set the backend assumes a default one
* @param {string=} params.start - Starting date to query the logs in ISO format
* @param {string=} params.end - Ending date to query the logs in ISO format
* @returns {Promise} Axios promise for the result of a GET request of logs
*/
getPodLogs({ environment, podName, containerName, search, start, end }) {
const url = this.buildUrl(environment.logs_api_path);
const params = {};
if (podName) {
params.pod_name = podName;
}
if (containerName) {
params.container_name = containerName;
}
if (search) {
params.search = search;
}
if (start) {
params.start = start;
}
if (end) {
params.end = end;
}
return axios.get(url, { params });
},
}; };
export default Api; export default Api;
import Api from 'ee/api'; import Api from '~/api';
import { backOff } from '~/lib/utils/common_utils'; import { backOff } from '~/lib/utils/common_utils';
import httpStatusCodes from '~/lib/utils/http_status'; import httpStatusCodes from '~/lib/utils/http_status';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
......
import logsBundle from 'ee/logs'; import logsBundle from '~/logs';
document.addEventListener('DOMContentLoaded', logsBundle); document.addEventListener('DOMContentLoaded', logsBundle);
...@@ -357,3 +357,42 @@ ...@@ -357,3 +357,42 @@
} }
} }
} }
.build-page-pod-logs {
.build-trace-container {
position: relative;
}
.build-trace {
@include build-trace();
}
.top-bar {
@include build-trace-top-bar($gl-line-height * 5);
.dropdown-menu-toggle {
width: 200px;
@include media-breakpoint-up(sm) {
width: 300px;
}
}
.controllers {
@include build-controllers(16px, flex-end, true, 2);
}
.refresh-control {
@include build-controllers(16px, flex-end, true, 0);
margin-left: 2px;
}
}
.btn-refresh svg {
top: 0;
}
.build-loader-animation {
@include build-loader-animation;
}
}
...@@ -41,4 +41,13 @@ module EnvironmentsHelper ...@@ -41,4 +41,13 @@ module EnvironmentsHelper
"external-dashboard-url" => project.metrics_setting_external_dashboard_url "external-dashboard-url" => project.metrics_setting_external_dashboard_url
} }
end end
def environment_logs_data(project, environment)
{
"environment-name": environment.name,
"environments-path": project_environments_path(project, format: :json),
"environment-id": environment.id,
"cluster-applications-documentation-path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack')
}
end
end end
...@@ -330,6 +330,10 @@ class Environment < ApplicationRecord ...@@ -330,6 +330,10 @@ class Environment < ApplicationRecord
self.auto_stop_at = parsed_result.seconds.from_now self.auto_stop_at = parsed_result.seconds.from_now
end end
def elastic_stack_available?
!!deployment_platform&.cluster&.application_elastic_stack&.available?
end
private private
def has_metrics_and_can_query? def has_metrics_and_can_query?
......
...@@ -314,6 +314,7 @@ class ProjectPolicy < BasePolicy ...@@ -314,6 +314,7 @@ class ProjectPolicy < BasePolicy
enable :admin_operations enable :admin_operations
enable :read_deploy_token enable :read_deploy_token
enable :create_deploy_token enable :create_deploy_token
enable :read_pod_logs
end end
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
......
...@@ -47,6 +47,22 @@ class EnvironmentEntity < Grape::Entity ...@@ -47,6 +47,22 @@ class EnvironmentEntity < Grape::Entity
environment.available? && can?(current_user, :stop_environment, environment) environment.available? && can?(current_user, :stop_environment, environment)
end end
expose :logs_path, if: -> (*) { can_read_pod_logs? } do |environment|
project_logs_path(environment.project, environment_name: environment.name)
end
expose :logs_api_path, if: -> (*) { can_read_pod_logs? } do |environment|
if environment.elastic_stack_available?
elasticsearch_project_logs_path(environment.project, environment_name: environment.name, format: :json)
else
k8s_project_logs_path(environment.project, environment_name: environment.name, format: :json)
end
end
expose :enable_advanced_logs_querying, if: -> (*) { can_read_pod_logs? } do |environment|
environment.elastic_stack_available?
end
private private
alias_method :environment, :object alias_method :environment, :object
...@@ -63,6 +79,10 @@ class EnvironmentEntity < Grape::Entity ...@@ -63,6 +79,10 @@ class EnvironmentEntity < Grape::Entity
can?(current_user, :update_environment, environment) can?(current_user, :update_environment, environment)
end end
def can_read_pod_logs?
can?(current_user, :read_pod_logs, environment.project)
end
def cluster_platform_kubernetes? def cluster_platform_kubernetes?
deployment_platform && deployment_platform.is_a?(Clusters::Platforms::Kubernetes) deployment_platform && deployment_platform.is_a?(Clusters::Platforms::Kubernetes)
end end
......
...@@ -263,7 +263,11 @@ ...@@ -263,7 +263,11 @@
%span %span
= _('Serverless') = _('Serverless')
= render_if_exists 'layouts/nav/sidebar/pod_logs_link' # EE-specific - if project_nav_tab?(:environments) && can?(current_user, :read_pod_logs, @project)
= nav_link(controller: :logs, action: [:index]) do
= link_to project_logs_path(@project), title: _('Logs') do
%span
= _('Logs')
- if project_nav_tab? :clusters - if project_nav_tab? :clusters
- show_cluster_hint = show_gke_cluster_integration_callout?(@project) - show_cluster_hint = show_gke_cluster_integration_callout?(@project)
......
---
title: Move pod logs to core
merge_request: 25455
author:
type: changed
...@@ -175,6 +175,13 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -175,6 +175,13 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
resources :logs, only: [:index] do
collection do
get :k8s
get :elasticsearch
end
end
resources :starrers, only: [:index] resources :starrers, only: [:index]
resources :forks, only: [:index, :new, :create] resources :forks, only: [:index, :new, :create]
resources :group_links, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ } resources :group_links, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ }
......
...@@ -88,12 +88,13 @@ dropdown box above the upper right corner of the panel: ...@@ -88,12 +88,13 @@ dropdown box above the upper right corner of the panel:
The options are: The options are:
- [View logs](#view-logs-ultimate) **(ULTIMATE)** - [View logs](#view-logs)
- [Download CSV](#download-csv) - [Download CSV](#download-csv)
##### View logs **(ULTIMATE)** ##### View logs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/201846) in GitLab Ultimate 12.8. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/201846) in GitLab Ultimate 12.8.
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25455) to [GitLab Core](https://about.gitlab.com/pricing/) 12.9.
This can be useful if you are triaging an application incident and need to This can be useful if you are triaging an application incident and need to
[explore logs](../project/integrations/prometheus.md#view-logs-ultimate) [explore logs](../project/integrations/prometheus.md#view-logs-ultimate)
......
...@@ -27,7 +27,7 @@ Using the GitLab project Kubernetes integration, you can: ...@@ -27,7 +27,7 @@ Using the GitLab project Kubernetes integration, you can:
- Use [Web terminals](#web-terminals). - Use [Web terminals](#web-terminals).
- Use [Deploy Boards](#deploy-boards-premium). **(PREMIUM)** - Use [Deploy Boards](#deploy-boards-premium). **(PREMIUM)**
- Use [Canary Deployments](#canary-deployments-premium). **(PREMIUM)** - Use [Canary Deployments](#canary-deployments-premium). **(PREMIUM)**
- View [Logs](#logs-ultimate). **(ULTIMATE)** - View [Logs](#logs).
- Run serverless workloads on [Kubernetes with Knative](serverless/index.md). - Run serverless workloads on [Kubernetes with Knative](serverless/index.md).
### Deploy Boards **(PREMIUM)** ### Deploy Boards **(PREMIUM)**
...@@ -48,7 +48,7 @@ the need to leave GitLab. ...@@ -48,7 +48,7 @@ the need to leave GitLab.
[Read more about Canary Deployments](../canary_deployments.md) [Read more about Canary Deployments](../canary_deployments.md)
### Logs **(ULTIMATE)** ### Logs
GitLab makes it easy to view the logs of running pods in connected Kubernetes clusters. By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface. GitLab makes it easy to view the logs of running pods in connected Kubernetes clusters. By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface.
......
# Kubernetes Logs **(ULTIMATE)** # Kubernetes Logs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4752) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4752) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.0.
> [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25455) to [GitLab Core](https://about.gitlab.com/pricing/) 12.9.
GitLab makes it easy to view the logs of running pods in [connected Kubernetes clusters](index.md). GitLab makes it easy to view the logs of running pods in [connected Kubernetes clusters](index.md).
By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface. By displaying the logs directly in GitLab, developers can avoid having to manage console tools or jump to a different interface.
......
...@@ -90,41 +90,6 @@ export default { ...@@ -90,41 +90,6 @@ export default {
return axios.delete(url); return axios.delete(url);
}, },
/**
* Returns pods logs for an environment with an optional pod and container
*
* @param {Object} params
* @param {Object} param.environment - Environment object
* @param {string=} params.podName - Pod name, if not set the backend assumes a default one
* @param {string=} params.containerName - Container name, if not set the backend assumes a default one
* @param {string=} params.start - Starting date to query the logs in ISO format
* @param {string=} params.end - Ending date to query the logs in ISO format
* @returns {Promise} Axios promise for the result of a GET request of logs
*/
getPodLogs({ environment, podName, containerName, search, start, end }) {
const url = this.buildUrl(environment.logs_api_path);
const params = {};
if (podName) {
params.pod_name = podName;
}
if (containerName) {
params.container_name = containerName;
}
if (search) {
params.search = search;
}
if (start) {
params.start = start;
}
if (end) {
params.end = end;
}
return axios.get(url, { params });
},
groupPackages(id, options = {}) { groupPackages(id, options = {}) {
const url = Api.buildUrl(this.groupPackagesPath).replace(':id', id); const url = Api.buildUrl(this.groupPackagesPath).replace(':id', id);
return axios.get(url, options); return axios.get(url, options);
......
.build-page-pod-logs {
.build-trace-container {
position: relative;
}
.build-trace {
@include build-trace();
}
.top-bar {
@include build-trace-top-bar($gl-line-height * 5);
.dropdown-menu-toggle {
width: 200px;
@include media-breakpoint-up(sm) {
width: 300px;
}
}
.controllers {
@include build-controllers(16px, flex-end, true, 2);
}
.refresh-control {
@include build-controllers(16px, flex-end, true, 0);
margin-left: 2px;
}
}
.btn-refresh svg {
top: 0;
}
.build-loader-animation {
@include build-loader-animation;
}
}
...@@ -30,15 +30,6 @@ module EE ...@@ -30,15 +30,6 @@ module EE
project.feature_available?(:custom_prometheus_metrics) && can?(current_user, :admin_project, project) project.feature_available?(:custom_prometheus_metrics) && can?(current_user, :admin_project, project)
end end
def environment_logs_data(project, environment)
{
"environment-name": environment.name,
"environments-path": project_environments_path(project, format: :json),
"environment-id": environment.id,
"cluster-applications-documentation-path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack')
}
end
def metrics_data(project, environment) def metrics_data(project, environment)
ee_metrics_data = { ee_metrics_data = {
"custom-metrics-path" => project_prometheus_metrics_path(project), "custom-metrics-path" => project_prometheus_metrics_path(project),
......
...@@ -77,10 +77,6 @@ module EE ...@@ -77,10 +77,6 @@ module EE
result || ::Gitlab::Kubernetes::RolloutStatus.loading result || ::Gitlab::Kubernetes::RolloutStatus.loading
end end
def elastic_stack_available?
!!deployment_platform&.cluster&.application_elastic_stack&.available?
end
private private
def rollout_status_available? def rollout_status_available?
......
...@@ -121,7 +121,6 @@ class License < ApplicationRecord ...@@ -121,7 +121,6 @@ class License < ApplicationRecord
license_management license_management
license_scanning license_scanning
personal_access_token_expiration_policy personal_access_token_expiration_policy
pod_logs
prometheus_alerts prometheus_alerts
pseudonymizer pseudonymizer
report_approver_rules report_approver_rules
......
...@@ -82,11 +82,6 @@ module EE ...@@ -82,11 +82,6 @@ module EE
@subject.feature_available?(:reject_unsigned_commits) @subject.feature_available?(:reject_unsigned_commits)
end end
with_scope :subject
condition(:pod_logs_enabled) do
@subject.feature_available?(:pod_logs, @user)
end
with_scope :subject with_scope :subject
condition(:security_dashboard_enabled) do condition(:security_dashboard_enabled) do
@subject.feature_available?(:security_dashboard) @subject.feature_available?(:security_dashboard)
...@@ -235,7 +230,6 @@ module EE ...@@ -235,7 +230,6 @@ module EE
rule { license_scanning_enabled & can?(:maintainer_access) }.enable :admin_software_license_policy rule { license_scanning_enabled & can?(:maintainer_access) }.enable :admin_software_license_policy
rule { pod_logs_enabled & can?(:maintainer_access) }.enable :read_pod_logs
rule { prometheus_alerts_enabled & can?(:maintainer_access) }.enable :read_prometheus_alerts rule { prometheus_alerts_enabled & can?(:maintainer_access) }.enable :read_prometheus_alerts
rule { auditor }.policy do rule { auditor }.policy do
......
...@@ -7,30 +7,10 @@ module EE ...@@ -7,30 +7,10 @@ module EE
prepended do prepended do
expose :rollout_status, if: -> (*) { can_read_deploy_board? }, using: ::RolloutStatusEntity expose :rollout_status, if: -> (*) { can_read_deploy_board? }, using: ::RolloutStatusEntity
expose :logs_path, if: -> (*) { can_read_pod_logs? } do |environment|
project_logs_path(environment.project, environment_name: environment.name)
end
expose :logs_api_path, if: -> (*) { can_read_pod_logs? } do |environment|
if environment.elastic_stack_available?
elasticsearch_project_logs_path(environment.project, environment_name: environment.name, format: :json)
else
k8s_project_logs_path(environment.project, environment_name: environment.name, format: :json)
end
end
expose :enable_advanced_logs_querying, if: -> (*) { can_read_pod_logs? } do |environment|
environment.elastic_stack_available?
end
end end
private private
def can_read_pod_logs?
can?(current_user, :read_pod_logs, environment.project)
end
def can_read_deploy_board? def can_read_deploy_board?
can?(current_user, :read_deploy_board, environment.project) can?(current_user, :read_deploy_board, environment.project)
end end
......
- return unless can?(current_user, :read_pod_logs, @project)
- return unless project_nav_tab?(:environments)
= nav_link(controller: :logs, action: [:index]) do
= link_to project_logs_path(@project), title: _('Logs') do
%span
= _('Logs')
...@@ -73,13 +73,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -73,13 +73,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :threat_monitoring, only: [:show], controller: :threat_monitoring resource :threat_monitoring, only: [:show], controller: :threat_monitoring
resources :logs, only: [:index] do
collection do
get :k8s
get :elasticsearch
end
end
resources :protected_environments, only: [:create, :update, :destroy], constraints: { id: /\d+/ } do resources :protected_environments, only: [:create, :update, :destroy], constraints: { id: /\d+/ } do
collection do collection do
get 'search' get 'search'
......
...@@ -14,8 +14,6 @@ describe 'Environment > Pod Logs', :js do ...@@ -14,8 +14,6 @@ describe 'Environment > Pod Logs', :js do
let(:service) { create(:cluster_platform_kubernetes, :configured) } let(:service) { create(:cluster_platform_kubernetes, :configured) }
before do before do
stub_licensed_features(pod_logs: true)
create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project]) create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project])
create(:deployment, :success, environment: environment) create(:deployment, :success, environment: environment)
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
{ "$ref": "../rollout_status.json" } { "$ref": "../rollout_status.json" }
] ]
}, },
"logs_path": { "type": "string" },
"updated_at": { "type": "date" } "updated_at": { "type": "date" }
}, },
"additionalProperties": false "additionalProperties": false
......
...@@ -57,6 +57,15 @@ ...@@ -57,6 +57,15 @@
"folder_path": { "folder_path": {
"type": "string" "type": "string"
}, },
"logs_path": {
"type": "string"
},
"logs_api_path": {
"type": "string"
},
"enable_advanced_logs_querying": {
"type": "boolean"
},
"created_at": { "created_at": {
"type": "date" "type": "date"
}, },
......
...@@ -229,39 +229,4 @@ describe Environment, :use_clean_rails_memory_store_caching do ...@@ -229,39 +229,4 @@ describe Environment, :use_clean_rails_memory_store_caching do
end end
end end
end end
describe '#elastic_stack_available?' do
let!(:cluster) { create(:cluster, :project, :provided_by_user, projects: [project]) }
let!(:deployment) { create(:deployment, :success, environment: environment, project: project) }
context 'when app does not exist' do
it 'returns false' do
expect(environment.elastic_stack_available?).to be(false)
end
end
context 'when app exists' do
let!(:application) { create(:clusters_applications_elastic_stack, cluster: cluster) }
it 'returns false' do
expect(environment.elastic_stack_available?).to be(false)
end
end
context 'when app is installed' do
let!(:application) { create(:clusters_applications_elastic_stack, :installed, cluster: cluster) }
it 'returns true' do
expect(environment.elastic_stack_available?).to be(true)
end
end
context 'when app is updated' do
let!(:application) { create(:clusters_applications_elastic_stack, :updated, cluster: cluster) }
it 'returns true' do
expect(environment.elastic_stack_available?).to be(true)
end
end
end
end end
...@@ -26,12 +26,16 @@ describe Groups::ClustersController do ...@@ -26,12 +26,16 @@ describe Groups::ClustersController do
it 'avoids N+1 database queries' do it 'avoids N+1 database queries' do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { go }.count control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { go }.count
deployment_count = 2
create_list(:deployment, 2, :success, cluster: cluster) create_list(:deployment, deployment_count, :success, cluster: cluster)
# TODO remove this leeway when we refactor away from deployment_platform # TODO remove this leeway when we refactor away from deployment_platform
# (https://gitlab.com/gitlab-org/gitlab/issues/13635) # (https://gitlab.com/gitlab-org/gitlab/issues/13635)
leeway = 5 leeway = deployment_count * 2
# it also appears that `can_read_pod_logs?` in ee/app/serializers/clusters/environment_entity.rb
# generates 3 additional queries per deployment
leeway += deployment_count * 3
expect { go }.not_to exceed_all_query_limit(control_count + leeway) expect { go }.not_to exceed_all_query_limit(control_count + leeway)
end end
end end
......
...@@ -51,21 +51,8 @@ describe Clusters::EnvironmentEntity do ...@@ -51,21 +51,8 @@ describe Clusters::EnvironmentEntity do
end end
end end
context 'when pod_logs are available' do
before do
stub_licensed_features(pod_logs: true)
project.add_maintainer(user)
end
it 'exposes logs_path' do it 'exposes logs_path' do
expect(subject).to include(:logs_path) expect(subject).to include(:logs_path)
end end
end end
context 'when pod_logs are not available' do
it 'does not expose logs_path' do
expect(subject).not_to include(:logs_path)
end
end
end
end end
...@@ -4,7 +4,6 @@ require 'spec_helper' ...@@ -4,7 +4,6 @@ require 'spec_helper'
describe EnvironmentEntity do describe EnvironmentEntity do
include KubernetesHelpers include KubernetesHelpers
include Gitlab::Routing.url_helpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:environment) { create(:environment) } let(:environment) { create(:environment) }
...@@ -46,39 +45,5 @@ describe EnvironmentEntity do ...@@ -46,39 +45,5 @@ describe EnvironmentEntity do
expect(subject).not_to include(:rollout_status) expect(subject).not_to include(:rollout_status)
end end
end end
context 'when pod_logs are available' do
before do
stub_licensed_features(pod_logs: true)
end
it 'exposes logs keys' do
expect(subject).to include(:logs_path)
expect(subject).to include(:logs_api_path)
expect(subject).to include(:enable_advanced_logs_querying)
end
it 'uses k8s api when ES is not available' do
expect(subject[:logs_api_path]).to eq(k8s_project_logs_path(environment.project, environment_name: environment.name, format: :json))
end
it 'uses ES api when ES is available' do
allow(environment).to receive(:elastic_stack_available?).and_return(true)
expect(subject[:logs_api_path]).to eq(elasticsearch_project_logs_path(environment.project, environment_name: environment.name, format: :json))
end
end
context 'when pod_logs are not available' do
before do
stub_licensed_features(pod_logs: false)
end
it 'does not expose logs keys' do
expect(subject).not_to include(:logs_path)
expect(subject).not_to include(:logs_api_path)
expect(subject).not_to include(:enable_advanced_logs_querying)
end
end
end end
end end
...@@ -22,23 +22,6 @@ describe Projects::LogsController do ...@@ -22,23 +22,6 @@ describe Projects::LogsController do
end end
describe 'GET #index' do describe 'GET #index' do
context 'when unlicensed' do
before do
stub_licensed_features(pod_logs: false)
end
it 'renders forbidden' do
get :index, params: environment_params
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when licensed' do
before do
stub_licensed_features(pod_logs: true)
end
let(:empty_project) { create(:project) } let(:empty_project) { create(:project) }
it 'renders empty logs page if no environment exists' do it 'renders empty logs page if no environment exists' do
...@@ -56,7 +39,6 @@ describe Projects::LogsController do ...@@ -56,7 +39,6 @@ describe Projects::LogsController do
expect(response).to render_template 'index' expect(response).to render_template 'index'
end end
end end
end
shared_examples 'pod logs service' do |endpoint, service| shared_examples 'pod logs service' do |endpoint, service|
let(:service_result) do let(:service_result) do
...@@ -73,21 +55,11 @@ describe Projects::LogsController do ...@@ -73,21 +55,11 @@ describe Projects::LogsController do
let_it_be(:cluster) { create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project]) } let_it_be(:cluster) { create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [project]) }
before do before do
stub_licensed_features(pod_logs: true)
allow_next_instance_of(service) do |instance| allow_next_instance_of(service) do |instance|
allow(instance).to receive(:execute).and_return(service_result) allow(instance).to receive(:execute).and_return(service_result)
end end
end end
it 'returns 404 when unlicensed' do
stub_licensed_features(pod_logs: false)
get endpoint, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns the service result' do it 'returns the service result' do
get endpoint, params: environment_params(pod_name: pod_name, format: :json) get endpoint, params: environment_params(pod_name: pod_name, format: :json)
......
...@@ -70,6 +70,7 @@ describe 'Project navbar' do ...@@ -70,6 +70,7 @@ describe 'Project navbar' do
_('Environments'), _('Environments'),
_('Error Tracking'), _('Error Tracking'),
_('Serverless'), _('Serverless'),
_('Logs'),
_('Kubernetes') _('Kubernetes')
] ]
}, },
......
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
"stop_path": { "type": "string" }, "stop_path": { "type": "string" },
"cancel_auto_stop_path": { "type": "string" }, "cancel_auto_stop_path": { "type": "string" },
"folder_path": { "type": "string" }, "folder_path": { "type": "string" },
"logs_path": { "type": "string" },
"logs_api_path": { "type": "string" },
"enable_advanced_logs_querying": { "type": "boolean" },
"created_at": { "type": "string", "format": "date-time" }, "created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }, "updated_at": { "type": "string", "format": "date-time" },
"auto_stop_at": { "type": "string", "format": "date-time" }, "auto_stop_at": { "type": "string", "format": "date-time" },
......
...@@ -2,9 +2,9 @@ import Vue from 'vue'; ...@@ -2,9 +2,9 @@ import Vue from 'vue';
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue'; import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import EnvironmentLogs from 'ee/logs/components/environment_logs.vue'; import EnvironmentLogs from '~/logs/components/environment_logs.vue';
import { createStore } from 'ee/logs/stores'; import { createStore } from '~/logs/stores';
import { scrollDown } from '~/lib/utils/scroll_utils'; import { scrollDown } from '~/lib/utils/scroll_utils';
import { import {
mockEnvName, mockEnvName,
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import LogControlButtons from 'ee/logs/components/log_control_buttons.vue'; import LogControlButtons from '~/logs/components/log_control_buttons.vue';
import { import {
canScroll, canScroll,
isScrolledToTop, isScrolledToTop,
......
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import * as types from 'ee/logs/stores/mutation_types'; import * as types from '~/logs/stores/mutation_types';
import { convertToFixedRange } from '~/lib/utils/datetime_range'; import { convertToFixedRange } from '~/lib/utils/datetime_range';
import logsPageState from 'ee/logs/stores/state'; import logsPageState from '~/logs/stores/state';
import { import {
setInitData, setInitData,
setSearch, setSearch,
showPodLogs, showPodLogs,
fetchEnvironments, fetchEnvironments,
fetchLogs, fetchLogs,
} from 'ee/logs/stores/actions'; } from '~/logs/stores/actions';
import { defaultTimeRange } from '~/monitoring/constants'; import { defaultTimeRange } from '~/monitoring/constants';
...@@ -30,7 +30,7 @@ import { ...@@ -30,7 +30,7 @@ import {
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/lib/utils/datetime_range'); jest.mock('~/lib/utils/datetime_range');
jest.mock('ee/logs/utils'); jest.mock('~/logs/utils');
const mockDefaultRange = { const mockDefaultRange = {
start: '2020-01-10T18:00:00.000Z', start: '2020-01-10T18:00:00.000Z',
......
import * as getters from 'ee/logs/stores/getters'; import * as getters from '~/logs/stores/getters';
import logsPageState from 'ee/logs/stores/state'; import logsPageState from '~/logs/stores/state';
import { mockLogsResult, mockTrace } from '../mock_data'; import { mockLogsResult, mockTrace } from '../mock_data';
......
import mutations from 'ee/logs/stores/mutations'; import mutations from '~/logs/stores/mutations';
import * as types from 'ee/logs/stores/mutation_types'; import * as types from '~/logs/stores/mutation_types';
import logsPageState from 'ee/logs/stores/state'; import logsPageState from '~/logs/stores/state';
import { import {
mockEnvName, mockEnvName,
mockEnvironments, mockEnvironments,
......
import { getTimeRange } from 'ee/logs/utils'; import { getTimeRange } from '~/logs/utils';
describe('logs/utils', () => { describe('logs/utils', () => {
describe('getTimeRange', () => { describe('getTimeRange', () => {
......
...@@ -10,7 +10,7 @@ describe Gitlab::Elasticsearch::Logs do ...@@ -10,7 +10,7 @@ describe Gitlab::Elasticsearch::Logs do
let(:es_message_3) { { timestamp: "2019-12-13T14:35:36.034Z", message: "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13" } } let(:es_message_3) { { timestamp: "2019-12-13T14:35:36.034Z", message: "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13" } }
let(:es_message_4) { { timestamp: "2019-12-13T14:35:37.034Z", message: "- -\u003e /" } } let(:es_message_4) { { timestamp: "2019-12-13T14:35:37.034Z", message: "- -\u003e /" } }
let(:es_response) { JSON.parse(fixture_file('lib/elasticsearch/logs_response.json', dir: 'ee')) } let(:es_response) { JSON.parse(fixture_file('lib/elasticsearch/logs_response.json')) }
subject { described_class.new(client) } subject { described_class.new(client) }
...@@ -21,12 +21,12 @@ describe Gitlab::Elasticsearch::Logs do ...@@ -21,12 +21,12 @@ describe Gitlab::Elasticsearch::Logs do
let(:start_time) { "2019-12-13T14:35:34.034Z" } let(:start_time) { "2019-12-13T14:35:34.034Z" }
let(:end_time) { "2019-12-13T14:35:34.034Z" } let(:end_time) { "2019-12-13T14:35:34.034Z" }
let(:body) { JSON.parse(fixture_file('lib/elasticsearch/query.json', dir: 'ee')) } let(:body) { JSON.parse(fixture_file('lib/elasticsearch/query.json')) }
let(:body_with_container) { JSON.parse(fixture_file('lib/elasticsearch/query_with_container.json', dir: 'ee')) } let(:body_with_container) { JSON.parse(fixture_file('lib/elasticsearch/query_with_container.json')) }
let(:body_with_search) { JSON.parse(fixture_file('lib/elasticsearch/query_with_search.json', dir: 'ee')) } let(:body_with_search) { JSON.parse(fixture_file('lib/elasticsearch/query_with_search.json')) }
let(:body_with_times) { JSON.parse(fixture_file('lib/elasticsearch/query_with_times.json', dir: 'ee')) } let(:body_with_times) { JSON.parse(fixture_file('lib/elasticsearch/query_with_times.json')) }
let(:body_with_start_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_start_time.json', dir: 'ee')) } let(:body_with_start_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_start_time.json')) }
let(:body_with_end_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_end_time.json', dir: 'ee')) } let(:body_with_end_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_end_time.json')) }
RSpec::Matchers.define :a_hash_equal_to_json do |expected| RSpec::Matchers.define :a_hash_equal_to_json do |expected|
match do |actual| match do |actual|
......
...@@ -1266,4 +1266,39 @@ describe Environment, :use_clean_rails_memory_store_caching do ...@@ -1266,4 +1266,39 @@ describe Environment, :use_clean_rails_memory_store_caching do
expect(env).to be_persisted expect(env).to be_persisted
end end
end end
describe '#elastic_stack_available?' do
let!(:cluster) { create(:cluster, :project, :provided_by_user, projects: [project]) }
let!(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) }
context 'when app does not exist' do
it 'returns false' do
expect(environment.elastic_stack_available?).to be(false)
end
end
context 'when app exists' do
let!(:application) { create(:clusters_applications_elastic_stack, cluster: cluster) }
it 'returns false' do
expect(environment.elastic_stack_available?).to be(false)
end
end
context 'when app is installed' do
let!(:application) { create(:clusters_applications_elastic_stack, :installed, cluster: cluster) }
it 'returns true' do
expect(environment.elastic_stack_available?).to be(true)
end
end
context 'when app is updated' do
let!(:application) { create(:clusters_applications_elastic_stack, :updated, cluster: cluster) }
it 'returns true' do
expect(environment.elastic_stack_available?).to be(true)
end
end
end
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe EnvironmentEntity do describe EnvironmentEntity do
include Gitlab::Routing.url_helpers
let(:request) { double('request') } let(:request) { double('request') }
let(:entity) do let(:entity) do
described_class.new(environment, request: spy('request')) described_class.new(environment, request: spy('request'))
...@@ -71,4 +73,22 @@ describe EnvironmentEntity do ...@@ -71,4 +73,22 @@ describe EnvironmentEntity do
expect(subject).to include(:cancel_auto_stop_path, :auto_stop_at) expect(subject).to include(:cancel_auto_stop_path, :auto_stop_at)
end end
end end
context 'pod_logs' do
it 'exposes logs keys' do
expect(subject).to include(:logs_path)
expect(subject).to include(:logs_api_path)
expect(subject).to include(:enable_advanced_logs_querying)
end
it 'uses k8s api when ES is not available' do
expect(subject[:logs_api_path]).to eq(k8s_project_logs_path(environment.project, environment_name: environment.name, format: :json))
end
it 'uses ES api when ES is available' do
allow(environment).to receive(:elastic_stack_available?).and_return(true)
expect(subject[:logs_api_path]).to eq(elasticsearch_project_logs_path(environment.project, environment_name: environment.name, format: :json))
end
end
end 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