Commit a2ffd9f6 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'ak/bubble-up-log-source' into 'master'

Pass log source to the frontend

See merge request gitlab-org/gitlab!22694
parents eac596f7 6869a1ff
---
title: Pass log source to the frontend
merge_request: 22694
author:
type: changed
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlDropdown, GlDropdownItem, GlFormGroup, GlSearchBoxByClick } from '@gitlab/ui';
import { GlDropdown, GlDropdownItem, GlFormGroup, GlSearchBoxByClick, GlAlert } from '@gitlab/ui';
import { scrollDown } from '~/lib/utils/scroll_utils';
import LogControlButtons from './log_control_buttons.vue';
export default {
components: {
GlAlert,
GlDropdown,
GlDropdownItem,
GlFormGroup,
......@@ -32,14 +33,19 @@ export default {
required: false,
default: '',
},
clusterApplicationsDocumentationPath: {
type: String,
required: true,
},
},
data() {
return {
searchQuery: '',
isElasticStackCalloutDismissed: false,
};
},
computed: {
...mapState('environmentLogs', ['environments', 'logs', 'pods']),
...mapState('environmentLogs', ['environments', 'logs', 'pods', 'enableAdvancedQuerying']),
...mapGetters('environmentLogs', ['trace']),
showLoader() {
return this.logs.isLoading || !this.logs.isComplete;
......@@ -47,6 +53,16 @@ export default {
featureElasticEnabled() {
return gon.features && gon.features.enableClusterApplicationElasticStack;
},
advancedFeaturesEnabled() {
return this.featureElasticEnabled && this.enableAdvancedQuerying;
},
shouldShowElasticStackCallout() {
return (
!this.isElasticStackCalloutDismissed &&
!this.environments.isLoading &&
!this.advancedFeaturesEnabled
);
},
},
watch: {
trace(val) {
......@@ -80,6 +96,22 @@ export default {
</script>
<template>
<div class="build-page-pod-logs mt-3">
<gl-alert
v-if="shouldShowElasticStackCallout"
class="mb-3"
@dismiss="isElasticStackCalloutDismissed = true"
>
{{
s__(
'Environments|Install Elastic Stack on your cluster to enable advanced querying capabilities such as full text search.',
)
}}
<a :href="clusterApplicationsDocumentationPath">
<strong>
{{ s__('View Documentation') }}
</strong>
</a>
</gl-alert>
<div class="top-bar js-top-bar d-flex">
<div class="row">
<gl-form-group
......@@ -138,7 +170,7 @@ export default {
>
<gl-search-box-by-click
v-model.trim="searchQuery"
:disabled="environments.isLoading"
:disabled="environments.isLoading || !advancedFeaturesEnabled"
:placeholder="s__('Environments|Search')"
class="js-logs-search"
type="search"
......
......@@ -71,7 +71,8 @@ export const fetchLogs = ({ commit, state }) => {
return requestLogsUntilData(params)
.then(({ data }) => {
const { pod_name, pods, logs } = data;
const { pod_name, pods, logs, enable_advanced_querying } = data;
commit(types.ENABLE_ADVANCED_QUERYING, enable_advanced_querying);
commit(types.SET_CURRENT_POD_NAME, pod_name);
commit(types.RECEIVE_PODS_DATA_SUCCESS, pods);
......
export const SET_PROJECT_PATH = 'SET_PROJECT_PATH';
export const SET_PROJECT_ENVIRONMENT = 'SET_PROJECT_ENVIRONMENT';
export const SET_SEARCH = 'SET_SEARCH';
export const ENABLE_ADVANCED_QUERYING = 'ENABLE_ADVANCED_QUERYING';
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
......
......@@ -11,6 +11,11 @@ export default {
state.search = searchQuery;
},
/** Log source supports advanced features */
[types.ENABLE_ADVANCED_QUERYING](state, enableAdvancedQuerying) {
state.enableAdvancedQuerying = enableAdvancedQuerying;
},
/** Environments data */
[types.SET_PROJECT_ENVIRONMENT](state, environmentName) {
state.environments.current = environmentName;
......
......@@ -9,6 +9,11 @@ export default () => ({
*/
search: '',
/**
* True if log source is elasticsearch
*/
enableAdvancedQuerying: false,
/**
* Environments list information
*/
......
......@@ -35,7 +35,8 @@ module EE
"environment-name": environment.name,
"environments-path": project_environments_path(project, format: :json),
"project-full-path": project.full_path,
"environment-id": environment.id
"environment-id": environment.id,
"cluster-applications-documentation-path" => help_page_path('user/clusters/applications.md', anchor: 'elastic-stack')
}
end
......
......@@ -85,7 +85,8 @@ module EE
private
def pod_logs(pod_name, namespace, container: nil, search: nil)
logs = if ::Feature.enabled?(:enable_cluster_application_elastic_stack) && elastic_stack_client
enable_advanced_querying = ::Feature.enabled?(:enable_cluster_application_elastic_stack) && !!elastic_stack_client
logs = if enable_advanced_querying
elastic_stack_pod_logs(namespace, pod_name, container, search)
else
platform_pod_logs(namespace, pod_name, container)
......@@ -95,7 +96,8 @@ module EE
logs: logs,
status: :success,
pod_name: pod_name,
container_name: container
container_name: container,
enable_advanced_querying: enable_advanced_querying
}
end
......
......@@ -9,7 +9,7 @@ class PodLogsService < ::BaseService
PARAMS = %w(pod_name container_name search).freeze
SUCCESS_RETURN_KEYS = [:status, :logs, :pod_name, :container_name, :pods].freeze
SUCCESS_RETURN_KEYS = [:status, :logs, :pod_name, :container_name, :pods, :enable_advanced_querying].freeze
steps :check_param_lengths,
:check_deployment_platform,
......@@ -83,7 +83,7 @@ class PodLogsService < ::BaseService
return { status: :processing } unless response
result.merge!(response.slice(:pod_name, :container_name, :logs))
result.merge!(response.slice(:pod_name, :container_name, :logs, :enable_advanced_querying))
if response[:status] == :error
error(response[:error]).reverse_merge(result)
......
......@@ -14,6 +14,7 @@ import {
mockTrace,
mockPodName,
mockEnvironmentsEndpoint,
mockDocumentationPath,
} from '../mock_data';
jest.mock('~/lib/utils/scroll_utils');
......@@ -28,6 +29,7 @@ describe('EnvironmentLogs', () => {
projectFullPath: mockProjectPath,
environmentName: mockEnvName,
environmentsPath: mockEnvironmentsEndpoint,
clusterApplicationsDocumentationPath: mockDocumentationPath,
};
const actionMocks = {
......@@ -168,12 +170,37 @@ describe('EnvironmentLogs', () => {
});
});
describe('ES enabled and legacy environment', () => {
beforeEach(() => {
state.pods.options = [];
state.logs.lines = [];
state.logs.isLoading = false;
state.environments.options = [];
state.environments.isLoading = false;
state.enableAdvancedQuerying = false;
gon.features = gon.features || {};
gon.features.enableClusterApplicationElasticStack = true;
initWrapper();
});
it('displays a disabled search bar', () => {
expect(findSearchBar().exists()).toEqual(true);
expect(findSearchBar().attributes('disabled')).toEqual('true');
});
});
describe('state with data', () => {
beforeEach(() => {
actionMocks.setInitData.mockImplementation(() => {
state.pods.options = mockPods;
state.environments.current = mockEnvName;
[state.pods.current] = state.pods.options;
state.enableAdvancedQuerying = true;
state.logs.isComplete = false;
state.logs.lines = mockLogsResult;
......@@ -233,7 +260,7 @@ describe('EnvironmentLogs', () => {
expect(trace.text().split('\n')).toEqual(mockTrace);
});
it('displays the search bar', () => {
it('displays an enabled search bar', () => {
expect(findSearchBar().exists()).toEqual(true);
expect(findSearchBar().attributes('disabled')).toEqual(undefined);
expect(wrapper.find('#search-fg').attributes('class')).toEqual('col-4');
......
......@@ -2,6 +2,8 @@ export const mockProjectPath = 'root/autodevops-deploy';
export const mockEnvName = 'production';
export const mockEnvironmentsEndpoint = `${mockProjectPath}/environments.json`;
export const mockEnvId = '99';
export const mockEnableAdvancedQuerying = true;
export const mockDocumentationPath = '/documentation.md';
const makeMockEnvironment = (id, name) => ({
id,
......
......@@ -23,6 +23,7 @@ import {
mockLogsResult,
mockEnvName,
mockSearch,
mockEnableAdvancedQuerying,
} from '../mock_data';
jest.mock('~/flash');
......@@ -143,6 +144,7 @@ describe('Logs Store actions', () => {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
enable_advanced_querying: mockEnableAdvancedQuerying,
});
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
......@@ -154,6 +156,7 @@ describe('Logs Store actions', () => {
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.ENABLE_ADVANCED_QUERYING, payload: mockEnableAdvancedQuerying },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
......@@ -180,6 +183,7 @@ describe('Logs Store actions', () => {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
enable_advanced_querying: mockEnableAdvancedQuerying,
});
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
......@@ -191,6 +195,7 @@ describe('Logs Store actions', () => {
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.ENABLE_ADVANCED_QUERYING, payload: mockEnableAdvancedQuerying },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
......@@ -210,6 +215,7 @@ describe('Logs Store actions', () => {
pod_name: mockPodName,
pods: mockPods,
logs: mockLogsResult,
enable_advanced_querying: mockEnableAdvancedQuerying,
});
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
......@@ -220,6 +226,7 @@ describe('Logs Store actions', () => {
[
{ type: types.REQUEST_PODS_DATA },
{ type: types.REQUEST_LOGS_DATA },
{ type: types.ENABLE_ADVANCED_QUERYING, payload: mockEnableAdvancedQuerying },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
{ type: types.RECEIVE_LOGS_DATA_SUCCESS, payload: mockLogsResult },
......
......@@ -10,6 +10,7 @@ import {
mockPodName,
mockLogsResult,
mockSearch,
mockEnableAdvancedQuerying,
} from '../mock_data';
describe('Logs Store Mutations', () => {
......@@ -124,6 +125,17 @@ describe('Logs Store Mutations', () => {
expect(state.pods.current).toEqual(mockPodName);
});
});
describe('ENABLE_ADVANCED_QUERYING', () => {
it('set advanced querying toggle', () => {
state.enableAdvancedQuerying = !mockEnableAdvancedQuerying;
mutations[types.ENABLE_ADVANCED_QUERYING](state, mockEnableAdvancedQuerying);
expect(state.enableAdvancedQuerying).toEqual(mockEnableAdvancedQuerying);
});
});
describe('REQUEST_PODS_DATA', () => {
it('receives log data error and stops loading', () => {
mutations[types.REQUEST_PODS_DATA](state);
......
......@@ -141,6 +141,7 @@ describe Clusters::Platforms::Kubernetes do
let(:pod_name) { 'pod-1' }
let(:namespace) { 'app' }
let(:container) { 'some-container' }
let(:enable_advanced_querying) { false }
let(:expected_logs) do
[
{ message: "Log 1", timestamp: "2019-12-13T14:04:22.123456Z" },
......@@ -157,6 +158,7 @@ describe Clusters::Platforms::Kubernetes do
expect(subject[:status]).to eq(:success)
expect(subject[:pod_name]).to eq(pod_name)
expect(subject[:container_name]).to eq(container)
expect(subject[:enable_advanced_querying]).to eq(enable_advanced_querying)
end
end
......@@ -175,6 +177,7 @@ describe Clusters::Platforms::Kubernetes do
context 'when ElasticSearch is enabled' do
let(:cluster) { create(:cluster, :project, platform_kubernetes: service) }
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: cluster) }
let(:enable_advanced_querying) { true }
before do
expect_any_instance_of(::Clusters::Applications::ElasticStack).to receive(:elasticsearch_client).at_least(:once).and_return(Elasticsearch::Transport::Client.new)
......
......@@ -14,6 +14,7 @@ describe PodLogsService do
let(:pods) { [pod_name] }
let(:container_name) { 'container-1' }
let(:search) { nil }
let(:enable_advanced_querying) { false }
let(:logs) { ['Log 1', 'Log 2', 'Log 3'] }
let(:result) { subject.execute }
......@@ -36,6 +37,7 @@ describe PodLogsService do
expect(result[:pods]).to eq(pods)
expect(result[:pod_name]).to eq(response_pod_name)
expect(result[:container_name]).to eq(container_name)
expect(result[:enable_advanced_querying]).to eq(enable_advanced_querying)
end
end
......@@ -61,7 +63,8 @@ describe PodLogsService do
status: :error,
error: message,
pod_name: response_pod_name,
container_name: container_name
container_name: container_name,
enable_advanced_querying: enable_advanced_querying
})
end
end
......@@ -74,7 +77,8 @@ describe PodLogsService do
status: :success,
logs: ["Log 1", "Log 2", "Log 3"],
pod_name: response_pod_name,
container_name: container_name
container_name: container_name,
enable_advanced_querying: enable_advanced_querying
})
end
end
......
......@@ -6999,6 +6999,9 @@ msgstr ""
msgid "Environments|Environments are places where code gets deployed, such as staging or production."
msgstr ""
msgid "Environments|Install Elastic Stack on your cluster to enable advanced querying capabilities such as full text search."
msgstr ""
msgid "Environments|Job"
msgstr ""
......@@ -20367,6 +20370,9 @@ msgstr ""
msgid "Very helpful"
msgstr ""
msgid "View Documentation"
msgstr ""
msgid "View app"
msgstr ""
......
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