Commit 75dfb915 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'ak/logs-search' into 'master'

Rework pod logs navigation scheme

See merge request gitlab-org/gitlab!20578
parents 06be63d0 46ad3339
......@@ -145,7 +145,8 @@ export default {
:is-loading="model.isLoadingDeployBoard"
:is-empty="model.isEmptyDeployBoard"
:has-legacy-app-label="model.hasLegacyAppLabel"
:logs-path="model.logs_path"
:project-path="model.project_path"
:environment-name="model.name"
/>
</div>
</div>
......
---
title: Rework pod logs navigation scheme
merge_request: 20578
author:
type: changed
......@@ -11,11 +11,7 @@ export default {
groupEpicsPath:
'/api/:version/groups/:id/epics?include_ancestor_groups=:includeAncestorGroups&include_descendant_groups=:includeDescendantGroups',
epicIssuePath: '/api/:version/groups/:id/epics/:epic_iid/issues/:issue_id',
podLogsPath: '/:project_full_path/-/environments/:environment_id/pods/containers/logs.json',
podLogsPathWithPod:
'/:project_full_path/-/environments/:environment_id/pods/:pod_name/containers/logs.json',
podLogsPathWithPodContainer:
'/:project_full_path/-/environments/:environment_id/pods/:pod_name/containers/:container_name/logs.json',
podLogsPath: '/:project_full_path/-/logs/k8s.json',
groupPackagesPath: '/api/:version/groups/:id/packages',
projectPackagesPath: '/api/:version/projects/:id/packages',
projectPackagePath: '/api/:version/projects/:id/packages/:package_id',
......@@ -96,25 +92,21 @@ export default {
* @param {string=} params.containerName - Container name, if not set the backend assumes a default one
* @returns {Promise} Axios promise for the result of a GET request of logs
*/
getPodLogs({ projectPath, environmentId, podName, containerName }) {
let logPath = this.podLogsPath;
if (podName && containerName) {
logPath = this.podLogsPathWithPodContainer;
} else if (podName) {
logPath = this.podLogsPathWithPod;
}
getPodLogs({ projectPath, environmentName, podName, containerName }) {
const url = this.buildUrl(this.podLogsPath).replace(':project_full_path', projectPath);
let url = this.buildUrl(logPath)
.replace(':project_full_path', projectPath)
.replace(':environment_id', environmentId);
const params = {
environment_name: environmentName,
};
if (podName) {
url = url.replace(':pod_name', podName);
params.pod_name = podName;
}
if (containerName) {
url = url.replace(':container_name', containerName);
params.container_name = containerName;
}
return axios.get(url);
return axios.get(url, { params });
},
groupPackages(id, options = {}) {
......
......@@ -143,7 +143,8 @@ export default {
:tooltip-text="instance.tooltip"
:pod-name="instance.pod_name"
:stable="instance.stable"
:logs-path="`${row.item.environmentPath}/logs`"
:project-path="`/${row.item.project.path_with_namespace}`"
:environment-name="row.item.name"
/>
</template>
</div>
......
......@@ -42,10 +42,13 @@ export default {
type: Boolean,
required: true,
},
logsPath: {
environmentName: {
type: String,
required: false,
default: '',
required: true,
},
projectPath: {
type: String,
required: true,
},
hasLegacyAppLabel: {
type: Boolean,
......@@ -140,8 +143,9 @@ export default {
:key="i"
:status="instance.status"
:tooltip-text="instance.tooltip"
:environment-name="environmentName"
:pod-name="instance.pod_name"
:logs-path="logsPath"
:project-path="projectPath"
:stable="instance.stable"
/>
</template>
......
......@@ -12,15 +12,11 @@ export default {
LogControlButtons,
},
props: {
environmentId: {
type: String,
required: true,
},
projectFullPath: {
type: String,
required: true,
},
currentEnvironmentName: {
environmentName: {
type: String,
required: false,
default: '',
......@@ -56,14 +52,19 @@ export default {
mounted() {
this.setInitData({
projectPath: this.projectFullPath,
environmentId: this.environmentId,
environmentName: this.environmentName,
podName: this.currentPodName,
});
this.fetchEnvironments(this.environmentsPath);
},
methods: {
...mapActions('environmentLogs', ['setInitData', 'showPodLogs', 'fetchEnvironments']),
...mapActions('environmentLogs', [
'setInitData',
'showPodLogs',
'showEnvironment',
'fetchEnvironments',
]),
},
};
</script>
......@@ -80,7 +81,7 @@ export default {
>
<gl-dropdown
id="environments-dropdown"
:text="currentEnvironmentName"
:text="environments.current"
:disabled="environments.isLoading"
class="d-flex js-environments-dropdown"
toggle-class="dropdown-menu-toggle"
......@@ -88,7 +89,7 @@ export default {
<gl-dropdown-item
v-for="env in environments.options"
:key="env.id"
:href="env.logs_path"
@click="showEnvironment(env.name)"
>
{{ env.name }}
</gl-dropdown-item>
......
......@@ -6,9 +6,9 @@ import flash from '~/flash';
import { s__ } from '~/locale';
import * as types from './mutation_types';
const requestLogsUntilData = ({ projectPath, environmentId, podName }) =>
const requestLogsUntilData = ({ projectPath, environmentName, podName }) =>
backOff((next, stop) => {
Api.getPodLogs({ projectPath, environmentId, podName })
Api.getPodLogs({ projectPath, environmentName, podName })
.then(res => {
if (res.status === httpStatusCodes.ACCEPTED) {
next();
......@@ -21,8 +21,9 @@ const requestLogsUntilData = ({ projectPath, environmentId, podName }) =>
});
});
export const setInitData = ({ dispatch, commit }, { projectPath, environmentId, podName }) => {
commit(types.SET_PROJECT_ENVIRONMENT, { projectPath, environmentId });
export const setInitData = ({ dispatch, commit }, { projectPath, environmentName, podName }) => {
commit(types.SET_PROJECT_PATH, projectPath);
commit(types.SET_PROJECT_ENVIRONMENT, environmentName);
commit(types.SET_CURRENT_POD_NAME, podName);
dispatch('fetchLogs');
};
......@@ -32,6 +33,12 @@ export const showPodLogs = ({ dispatch, commit }, podName) => {
dispatch('fetchLogs');
};
export const showEnvironment = ({ dispatch, commit }, environmentName) => {
commit(types.SET_PROJECT_ENVIRONMENT, environmentName);
commit(types.SET_CURRENT_POD_NAME, null);
dispatch('fetchLogs');
};
export const fetchEnvironments = ({ commit }, environmentsPath) => {
commit(types.REQUEST_ENVIRONMENTS_DATA);
......@@ -49,7 +56,7 @@ export const fetchEnvironments = ({ commit }, environmentsPath) => {
export const fetchLogs = ({ commit, state }) => {
const params = {
projectPath: state.projectPath,
environmentId: state.environments.current,
environmentName: state.environments.current,
podName: state.pods.current,
};
......
export const SET_PROJECT_PATH = 'SET_PROJECT_PATH';
export const SET_PROJECT_ENVIRONMENT = 'SET_PROJECT_ENVIRONMENT';
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
......
......@@ -2,12 +2,14 @@ import * as types from './mutation_types';
export default {
/** Project data */
[types.SET_PROJECT_ENVIRONMENT](state, { projectPath, environmentId }) {
[types.SET_PROJECT_PATH](state, projectPath) {
state.projectPath = projectPath;
state.environments.current = environmentId;
},
/** Environments data */
[types.SET_PROJECT_ENVIRONMENT](state, environmentName) {
state.environments.current = environmentName;
},
[types.REQUEST_ENVIRONMENTS_DATA](state) {
state.environments.options = [];
state.environments.isLoading = true;
......
......@@ -47,13 +47,19 @@ export default {
default: true,
},
environmentName: {
type: String,
required: false,
default: '',
},
podName: {
type: String,
required: false,
default: '',
},
logsPath: {
projectPath: {
type: String,
required: false,
default: '',
......@@ -74,7 +80,9 @@ export default {
},
computedLogPath() {
return this.isLink ? `${this.logsPath}?pod_name=${this.podName}` : null;
return this.isLink
? `${this.projectPath}/-/logs?environment_name=${this.environmentName}&pod_name=${this.podName}`
: null;
},
},
};
......
......@@ -6,49 +6,11 @@ module EE
extend ActiveSupport::Concern
prepended do
before_action :authorize_read_pod_logs!, only: [:k8s_pod_logs, :logs]
before_action :environment_ee, only: [:k8s_pod_logs, :logs]
before_action :authorize_create_environment_terminal!, only: [:terminal]
end
def logs_redirect
environment = project.default_environment
if environment
redirect_to logs_project_environment_path(project, environment)
else
render :empty_logs
end
end
def logs
end
def k8s_pod_logs
respond_to do |format|
format.json do
::Gitlab::UsageCounters::PodLogs.increment(project.id)
::Gitlab::PollingInterval.set_header(response, interval: 3_000)
result = PodLogsService.new(environment, params: params.permit!).execute
if result[:status] == :processing
head :accepted
elsif result[:status] == :success
render json: result
else
render status: :bad_request, json: result
end
end
end
end
private
def environment_ee
environment
end
def authorize_create_environment_terminal!
return render_404 unless can?(current_user, :create_environment_terminal, environment)
end
......
# frozen_string_literal: true
module Projects
class LogsController < Projects::ApplicationController
before_action :authorize_read_pod_logs!
before_action :environment
before_action do
push_frontend_feature_flag(:environment_logs_use_vue_ui)
end
def index
if environment.nil?
render :empty_logs
else
render :index
end
end
def k8s
::Gitlab::UsageCounters::PodLogs.increment(project.id)
::Gitlab::PollingInterval.set_header(response, interval: 3_000)
result = PodLogsService.new(environment, params: filter_params).execute
if result[:status] == :processing
head :accepted
elsif result[:status] == :success
render json: result
else
render status: :bad_request, json: result
end
end
private
def index_params
params.permit(:environment_name)
end
def filter_params
params.permit(:container_name, :pod_name)
end
def environment
@environment ||= if index_params.key?(:environment_name)
EnvironmentsFinder.new(project, current_user, name: index_params[:environment_name]).find.first
else
project.default_environment
end
end
end
end
......@@ -32,7 +32,7 @@ module EE
def environment_logs_data(project, environment)
{
"current-environment-name": environment.name,
"environment-name": environment.name,
"environments-path": project_environments_path(project, format: :json),
"project-full-path": project.full_path,
"environment-id": environment.id
......
......@@ -68,11 +68,11 @@ module EE
::Gitlab::EtagCaching::Store.new.tap do |store|
store.touch(
::Gitlab::Routing.url_helpers.k8s_pod_logs_project_environment_path(
::Gitlab::Routing.url_helpers.k8s_project_logs_path(
environment.project,
environment,
opts['pod_name'],
opts['container_name'],
environment_name: environment.name,
pod_name: opts['pod_name'],
container_name: opts['container_name'],
format: :json
)
)
......
......@@ -8,17 +8,13 @@ module EE
prepended do
expose :rollout_status, if: -> (*) { can_read_deploy_board? }, using: ::RolloutStatusEntity
expose :logs_path, if: -> (*) { can_read_pod_logs? } do |environment|
logs_project_environment_path(environment.project, environment)
expose :project_path do |environment|
project_path(environment.project)
end
end
private
def can_read_pod_logs?
can?(current_user, :read_pod_logs, environment.project)
end
def can_read_deploy_board?
can?(current_user, :read_deploy_board, environment.project)
end
......
- return unless can?(current_user, :read_pod_logs, @project)
- return unless project_nav_tab?(:environments)
= nav_link(controller: :environments, action: [:logs, :logs_redirect]) do
= link_to logs_project_environments_path(@project), title: _('Pod logs') do
= nav_link(controller: :logs, action: [:show]) do
= link_to project_logs_path(@project), title: _('Pod logs') do
%span
= _('Pod logs')
......@@ -70,14 +70,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :licenses, only: [:index, :create, :update]
end
resources :environments, only: [] do
member do
get :logs
get '/pods/(:pod_name)/containers/(:container_name)/logs', to: 'environments#k8s_pod_logs', as: :k8s_pod_logs
end
resources :logs, only: [:index] do
collection do
get :logs, action: :logs_redirect
get :k8s
end
end
......
......@@ -10,7 +10,7 @@ module EE
'epic_notes'
),
::Gitlab::EtagCaching::Router::Route.new(
%r(#{::Gitlab::EtagCaching::Router::RESERVED_WORDS_PREFIX}/environments/\d+/pods/(\S+/)?containers/(\S+/)?logs\.json\z),
%r(#{::Gitlab::EtagCaching::Router::RESERVED_WORDS_PREFIX}/logs/k8s\.json(\?.*)?\z),
'k8s_pod_logs'
)
].freeze
......
......@@ -76,169 +76,6 @@ describe Projects::EnvironmentsController do
end
end
describe 'GET #logs_redirect' do
let(:project) { create(:project) }
it 'redirects to environment if it exists' do
environment = create(:environment, name: 'production', project: project)
get :logs_redirect, params: { namespace_id: project.namespace, project_id: project }
expect(response).to redirect_to(logs_project_environment_path(project, environment))
end
it 'renders empty logs page if no environment exists' do
get :logs_redirect, params: { namespace_id: project.namespace, project_id: project }
expect(response).to be_ok
expect(response).to render_template 'empty_logs'
end
end
describe 'GET logs' do
let(:pod_name) { "foo" }
before do
stub_licensed_features(pod_logs: true)
end
context 'when unlicensed' do
before do
stub_licensed_features(pod_logs: false)
end
it 'renders forbidden' do
get :logs, params: environment_params(pod_name: pod_name)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when licensed' do
it 'renders logs template' do
get :logs, params: environment_params(pod_name: pod_name)
expect(response).to be_ok
expect(response).to render_template 'logs'
end
end
end
describe 'GET k8s_pod_logs' do
let(:pod_name) { "foo" }
let(:container) { 'container-1' }
let(:service_result) do
{
status: :success,
logs: ['Log 1', 'Log 2', 'Log 3'],
message: 'message',
pods: [pod_name],
pod_name: pod_name,
container_name: container
}
end
before do
stub_licensed_features(pod_logs: true)
allow_any_instance_of(PodLogsService).to receive(:execute).and_return(service_result)
end
shared_examples 'resource not found' do |message|
it 'returns 400', :aggregate_failures do
get :k8s_pod_logs, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq(message)
expect(json_response['pods']).to match_array([pod_name])
expect(json_response['pod_name']).to eq(pod_name)
expect(json_response['container_name']).to eq(container)
end
end
it 'returns the logs for a specific pod', :aggregate_failures do
get :k8s_pod_logs, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:success)
expect(json_response["logs"]).to match_array(["Log 1", "Log 2", "Log 3"])
expect(json_response["pods"]).to match_array([pod_name])
expect(json_response['message']).to eq(service_result[:message])
expect(json_response['pod_name']).to eq(pod_name)
expect(json_response['container_name']).to eq(container)
end
it 'registers a usage of the endpoint' do
expect(::Gitlab::UsageCounters::PodLogs).to receive(:increment).with(project.id)
get :k8s_pod_logs, params: environment_params(pod_name: pod_name, format: :json)
end
context 'when kubernetes API returns error' do
let(:service_result) do
{
status: :error,
message: 'Kubernetes API returned status code: 400',
pods: [pod_name],
pod_name: pod_name,
container_name: container
}
end
it 'returns bad request' do
get :k8s_pod_logs, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["logs"]).to eq(nil)
expect(json_response["pods"]).to match_array([pod_name])
expect(json_response["message"]).to eq('Kubernetes API returned status code: 400')
expect(json_response['pod_name']).to eq(pod_name)
expect(json_response['container_name']).to eq(container)
end
end
context 'when pod does not exist' do
let(:service_result) do
{
status: :error,
message: 'Pod not found',
pods: [pod_name],
pod_name: pod_name,
container_name: container
}
end
it_behaves_like 'resource not found', 'Pod not found'
end
context 'when service returns error without pods, pod_name, container_name' do
let(:service_result) do
{
status: :error,
message: 'No deployment platform'
}
end
it 'returns the error without pods, pod_name and container_name' do
get :k8s_pod_logs, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq('No deployment platform')
expect(json_response.keys).to contain_exactly('message', 'status')
end
end
context 'when service returns status processing' do
let(:service_result) { { status: :processing } }
it 'renders accepted' do
get :k8s_pod_logs, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:accepted)
end
end
end
describe '#GET terminal' do
let(:protected_environment) { create(:protected_environment, name: environment.name, project: project) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::LogsController do
include KubernetesHelpers
set(:user) { create(:user) }
set(:project) { create(:project) }
set(:environment) do
create(:environment, name: 'production', project: project)
end
let(:pod_name) { "foo" }
let(:container) { 'container-1' }
before do
project.add_maintainer(user)
sign_in(user)
end
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) }
it 'renders empty logs page if no environment exists' do
empty_project.add_maintainer(user)
get :index, params: { namespace_id: empty_project.namespace, project_id: empty_project }
expect(response).to be_ok
expect(response).to render_template 'empty_logs'
end
it 'renders index template' do
get :index, params: environment_params
expect(response).to be_ok
expect(response).to render_template 'index'
end
end
end
describe "GET #k8s" do
let(:service_result) do
{
status: :success,
logs: ['Log 1', 'Log 2', 'Log 3'],
message: 'message',
pods: [pod_name],
pod_name: pod_name,
container_name: container
}
end
before do
stub_licensed_features(pod_logs: true)
allow_any_instance_of(PodLogsService).to receive(:execute).and_return(service_result)
end
shared_examples 'resource not found' do |message|
it 'returns 400', :aggregate_failures do
get :k8s, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq(message)
expect(json_response['pods']).to match_array([pod_name])
expect(json_response['pod_name']).to eq(pod_name)
expect(json_response['container_name']).to eq(container)
end
end
it 'returns the logs for a specific pod', :aggregate_failures do
get :k8s, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:success)
expect(json_response["logs"]).to match_array(["Log 1", "Log 2", "Log 3"])
expect(json_response["pods"]).to match_array([pod_name])
expect(json_response['message']).to eq(service_result[:message])
expect(json_response['pod_name']).to eq(pod_name)
expect(json_response['container_name']).to eq(container)
end
it 'registers a usage of the endpoint' do
expect(::Gitlab::UsageCounters::PodLogs).to receive(:increment).with(project.id)
get :k8s, params: environment_params(pod_name: pod_name, format: :json)
end
context 'when kubernetes API returns error' do
let(:service_result) do
{
status: :error,
message: 'Kubernetes API returned status code: 400',
pods: [pod_name],
pod_name: pod_name,
container_name: container
}
end
it 'returns bad request' do
get :k8s, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["logs"]).to eq(nil)
expect(json_response["pods"]).to match_array([pod_name])
expect(json_response["message"]).to eq('Kubernetes API returned status code: 400')
expect(json_response['pod_name']).to eq(pod_name)
expect(json_response['container_name']).to eq(container)
end
end
context 'when pod does not exist' do
let(:service_result) do
{
status: :error,
message: 'Pod not found',
pods: [pod_name],
pod_name: pod_name,
container_name: container
}
end
it_behaves_like 'resource not found', 'Pod not found'
end
context 'when service returns error without pods, pod_name, container_name' do
let(:service_result) do
{
status: :error,
message: 'No deployment platform'
}
end
it 'returns the error without pods, pod_name and container_name' do
get :k8s, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq('No deployment platform')
expect(json_response.keys).to contain_exactly('message', 'status')
end
end
context 'when service returns status processing' do
let(:service_result) { { status: :processing } }
it 'renders accepted' do
get :k8s, params: environment_params(pod_name: pod_name, format: :json)
expect(response).to have_gitlab_http_status(:accepted)
end
end
end
def environment_params(opts = {})
opts.reverse_merge(namespace_id: project.namespace,
project_id: project,
environment_name: environment.name)
end
end
......@@ -37,7 +37,7 @@ describe 'Environment > Pod Logs', :js do
it "shows environments in dropdown" do
create(:environment, project: project)
visit logs_project_environment_path(environment.project, environment, pod_name: pod_name)
visit project_logs_path(environment.project, environment_name: environment.name, pod_name: pod_name)
wait_for_requests
......@@ -56,7 +56,7 @@ describe 'Environment > Pod Logs', :js do
context 'with logs', :use_clean_rails_memory_store_caching do
it "shows pod logs", :sidekiq_might_not_need_inline do
visit logs_project_environment_path(environment.project, environment, pod_name: pod_name)
visit project_logs_path(environment.project, environment_name: environment.name, pod_name: pod_name)
wait_for_requests
......@@ -95,7 +95,7 @@ describe 'Environment > Pod Logs', :js do
end
def load_and_scroll_down
visit logs_project_environment_path(environment.project, environment, pod_name: pod_name)
visit project_logs_path(environment.project, environment_name: environment.name, pod_name: pod_name)
wait_for_requests
......
{
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"state",
"last_deployment",
"environment_path",
"created_at",
"updated_at"
],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"name_without_type": {
"type": "string"
},
"state": {
"type": "string"
},
"external_url": {
"type": "string"
},
"environment_type": {
"type": [
"string",
"null"
]
},
"last_deployment": {
"oneOf": [
{
"$ref": "../../../../../spec/fixtures/api/schemas/deployment.json"
},
{
"type": ["null"]
}
]
},
"has_stop_action": {
"type": "boolean"
},
"rollout_status": {
"$ref": "rollout_status.json"
},
"environment_path": {
"type": "string"
},
"stop_path": {
"type": "string"
},
"terminal_path": {
"type": "string"
},
"folder_path": {
"type": "string"
},
"logs_path": {
"type": "string"
},
"created_at": {
"type": "date"
},
"updated_at": {
"type": "date"
},
"can_stop": {
"type": "boolean"
},
"cancel_auto_stop_path": { "type": "string" },
"auto_stop_at": { "type": "string", "format": "date-time" }
}
"type": "object",
"additionalProperties": false,
"required": [
"id",
"name",
"state",
"last_deployment",
"environment_path",
"created_at",
"updated_at"
],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"name_without_type": {
"type": "string"
},
"state": {
"type": "string"
},
"external_url": {
"type": "string"
},
"environment_type": {
"type": ["string", "null"]
},
"last_deployment": {
"oneOf": [
{
"$ref": "../../../../../spec/fixtures/api/schemas/deployment.json"
},
{
"type": ["null"]
}
]
},
"has_stop_action": {
"type": "boolean"
},
"rollout_status": {
"$ref": "rollout_status.json"
},
"environment_path": {
"type": "string"
},
"stop_path": {
"type": "string"
},
"terminal_path": {
"type": "string"
},
"folder_path": {
"type": "string"
},
"project_path": {
"type": "string"
},
"created_at": {
"type": "date"
},
"updated_at": {
"type": "date"
},
"can_stop": {
"type": "boolean"
},
"cancel_auto_stop_path": { "type": "string" },
"auto_stop_at": { "type": "string", "format": "date-time" }
}
}
......@@ -166,11 +166,11 @@ describe('Api', () => {
describe('getPodLogs', () => {
const projectPath = 'root/test-project';
const environmentId = 2;
const environmentName = 'production';
const podName = 'pod';
const containerName = 'container';
const lastUrl = () => mock.history.get[0].url;
const getRequest = () => mock.history.get[0];
beforeEach(() => {
mock.onAny().reply(200);
......@@ -181,33 +181,45 @@ describe('Api', () => {
});
it('calls `axios.get` with pod_name and container_name', done => {
const expectedUrl = `${dummyUrlRoot}/${projectPath}/-/environments/${environmentId}/pods/${podName}/containers/${containerName}/logs.json`;
const expectedUrl = `${dummyUrlRoot}/${projectPath}/-/logs/k8s.json`;
Api.getPodLogs({ projectPath, environmentId, podName, containerName })
Api.getPodLogs({ projectPath, environmentName, podName, containerName })
.then(() => {
expect(expectedUrl).toBe(lastUrl());
expect(getRequest().url).toBe(expectedUrl);
expect(getRequest().params).toEqual({
environment_name: environmentName,
pod_name: podName,
container_name: containerName,
});
})
.then(done)
.catch(done.fail);
});
it('calls `axios.get` without pod_name and container_name', done => {
const expectedUrl = `${dummyUrlRoot}/${projectPath}/-/environments/${environmentId}/pods/containers/logs.json`;
const expectedUrl = `${dummyUrlRoot}/${projectPath}/-/logs/k8s.json`;
Api.getPodLogs({ projectPath, environmentId })
Api.getPodLogs({ projectPath, environmentName })
.then(() => {
expect(expectedUrl).toBe(lastUrl());
expect(getRequest().url).toBe(expectedUrl);
expect(getRequest().params).toEqual({
environment_name: environmentName,
});
})
.then(done)
.catch(done.fail);
});
it('calls `axios.get` with pod_name', done => {
const expectedUrl = `${dummyUrlRoot}/${projectPath}/-/environments/${environmentId}/pods/${podName}/containers/logs.json`;
const expectedUrl = `${dummyUrlRoot}/${projectPath}/-/logs/k8s.json`;
Api.getPodLogs({ projectPath, environmentId, podName })
Api.getPodLogs({ projectPath, environmentName, podName })
.then(() => {
expect(expectedUrl).toBe(lastUrl());
expect(getRequest().url).toBe(expectedUrl);
expect(getRequest().params).toEqual({
environment_name: environmentName,
pod_name: podName,
});
})
.then(done)
.catch(done.fail);
......
......@@ -49,6 +49,7 @@ describe('Environment table', () => {
name: 'review',
size: 1,
environment_path: 'url',
project_path: 'url',
id: 1,
hasDeployBoard: true,
deployBoardData: deployBoardMockData,
......
......@@ -7,7 +7,6 @@ import { createStore } from 'ee/logs/stores';
import { scrollDown } from '~/lib/utils/scroll_utils';
import {
mockProjectPath,
mockEnvId,
mockEnvName,
mockEnvironments,
mockPods,
......@@ -26,14 +25,14 @@ describe('EnvironmentLogs', () => {
const propsData = {
projectFullPath: mockProjectPath,
environmentId: mockEnvId,
currentEnvironmentName: mockEnvName,
environmentName: mockEnvName,
environmentsPath: mockEnvironmentsEndpoint,
};
const actionMocks = {
setInitData: jest.fn(),
showPodLogs: jest.fn(),
showEnvironment: jest.fn(),
fetchEnvironments: jest.fn(),
};
......@@ -75,6 +74,10 @@ describe('EnvironmentLogs', () => {
actionMocks.setInitData.mockReset();
actionMocks.showPodLogs.mockReset();
actionMocks.fetchEnvironments.mockReset();
if (wrapper) {
wrapper.destroy();
}
});
it('displays UI elements', () => {
......@@ -95,16 +98,13 @@ describe('EnvironmentLogs', () => {
expect(actionMocks.setInitData).toHaveBeenCalledTimes(1);
expect(actionMocks.setInitData).toHaveBeenLastCalledWith({
environmentId: mockEnvId,
projectPath: mockProjectPath,
environmentName: mockEnvName,
podName: null,
});
expect(actionMocks.fetchEnvironments).toHaveBeenCalledTimes(1);
expect(actionMocks.fetchEnvironments).toHaveBeenLastCalledWith(mockEnvironmentsEndpoint);
expect(findEnvironmentsDropdown().props('text')).toBe(mockEnvName);
expect(findPodsDropdown().props('text').length).toBeGreaterThan(0);
});
describe('loading state', () => {
......@@ -148,6 +148,7 @@ describe('EnvironmentLogs', () => {
beforeEach(() => {
actionMocks.setInitData.mockImplementation(() => {
state.pods.options = mockPods;
state.environments.current = mockEnvName;
[state.pods.current] = state.pods.options;
state.logs.isComplete = false;
......@@ -178,14 +179,11 @@ describe('EnvironmentLogs', () => {
it('populates environments dropdown', () => {
const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
expect(findEnvironmentsDropdown().props('text')).toBe(mockEnvName);
expect(items.length).toBe(mockEnvironments.length);
mockEnvironments.forEach((env, i) => {
const item = items.at(i);
expect(item.text()).toBe(env.name);
expect(item.attributes('href')).toBe(env.logs_path);
});
});
......@@ -215,6 +213,18 @@ describe('EnvironmentLogs', () => {
});
describe('when user clicks', () => {
it('environment name, trace is refreshed', () => {
const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
const index = 1; // any env
expect(actionMocks.showEnvironment).toHaveBeenCalledTimes(0);
items.at(index).vm.$emit('click');
expect(actionMocks.showEnvironment).toHaveBeenCalledTimes(1);
expect(actionMocks.showEnvironment).toHaveBeenLastCalledWith(mockEnvironments[index].name);
});
it('pod name, trace is refreshed', () => {
const items = findPodsDropdown().findAll(GlDropdownItem);
const index = 2; // any pod
......
......@@ -10,12 +10,12 @@ import flash from '~/flash';
import {
mockProjectPath,
mockEnvId,
mockPodName,
mockEnvironmentsEndpoint,
mockEnvironments,
mockPods,
mockLines,
mockEnvName,
} from '../mock_data';
jest.mock('~/flash');
......@@ -36,13 +36,11 @@ describe('Logs Store actions', () => {
it('should commit environment and pod name mutation', done => {
testAction(
setInitData,
{ projectPath: mockProjectPath, environmentId: mockEnvId, podName: mockPodName },
{ projectPath: mockProjectPath, environmentName: mockEnvName, podName: mockPodName },
state,
[
{
type: types.SET_PROJECT_ENVIRONMENT,
payload: { projectPath: mockProjectPath, environmentId: mockEnvId },
},
{ type: types.SET_PROJECT_PATH, payload: mockProjectPath },
{ type: types.SET_PROJECT_ENVIRONMENT, payload: mockEnvName },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
],
[{ type: 'fetchLogs' }],
......@@ -114,16 +112,18 @@ describe('Logs Store actions', () => {
it('should commit logs and pod data when there is pod name defined', done => {
state.projectPath = mockProjectPath;
state.environments.current = mockEnvId;
state.environments.current = mockEnvName;
state.pods.current = mockPodName;
const endpoint = `/${mockProjectPath}/-/environments/${mockEnvId}/pods/${mockPodName}/containers/logs.json`;
const endpoint = `/${mockProjectPath}/-/logs/k8s.json`;
mock.onGet(endpoint).reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLines,
});
mock
.onGet(endpoint, { params: { environment_name: mockEnvName, pod_name: mockPodName } })
.reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLines,
});
mock.onGet(endpoint).replyOnce(202); // mock reactive cache
......@@ -145,11 +145,11 @@ describe('Logs Store actions', () => {
it('should commit logs and pod data when no pod name defined', done => {
state.projectPath = mockProjectPath;
state.environments.current = mockEnvId;
state.environments.current = mockEnvName;
const endpoint = `/${mockProjectPath}/-/environments/${mockEnvId}/pods/containers/logs.json`;
const endpoint = `/${mockProjectPath}/-/logs/k8s.json`;
mock.onGet(endpoint).reply(200, {
mock.onGet(endpoint, { params: { environment_name: mockEnvName } }).reply(200, {
pod_name: mockPodName,
pods: mockPods,
logs: mockLines,
......@@ -174,9 +174,9 @@ describe('Logs Store actions', () => {
it('should commit logs and pod errors when backend fails', done => {
state.projectPath = mockProjectPath;
state.environments.current = mockEnvId;
state.environments.current = mockEnvName;
const endpoint = `/${mockProjectPath}/-/environments/${mockEnvId}/pods/containers/logs.json`;
const endpoint = `/${mockProjectPath}/logs.json?environment_name=${mockEnvName}`;
mock.onGet(endpoint).replyOnce(500);
testAction(
......
......@@ -4,7 +4,7 @@ import * as types from 'ee/logs/stores/mutation_types';
import logsPageState from 'ee/logs/stores/state';
import {
mockProjectPath,
mockEnvId,
mockEnvName,
mockEnvironments,
mockPods,
mockPodName,
......@@ -25,13 +25,14 @@ describe('Logs Store Mutations', () => {
});
describe('SET_PROJECT_ENVIRONMENT', () => {
it('sets the logs json endpoint', () => {
mutations[types.SET_PROJECT_ENVIRONMENT](state, {
projectPath: mockProjectPath,
environmentId: mockEnvId,
});
it('sets the project path', () => {
mutations[types.SET_PROJECT_PATH](state, mockProjectPath);
expect(state.projectPath).toEqual(mockProjectPath);
expect(state.environments.current).toEqual(mockEnvId);
});
it('sets the environment', () => {
mutations[types.SET_PROJECT_ENVIRONMENT](state, mockEnvName);
expect(state.environments.current).toEqual(mockEnvName);
});
});
......
......@@ -36,7 +36,7 @@ describe EnvironmentsHelper do
it 'returns environment parameters data' do
expect(subject).to include(
"current-environment-name": environment.name,
"environment-name": environment.name,
"environments-path": project_environments_path(project, format: :json)
)
end
......
......@@ -4,6 +4,8 @@ import DeployBoard from 'ee/environments/components/deploy_board_component.vue';
import { environment } from 'spec/environments/mock_data';
import { deployBoardMockData } from './mock_data';
const projectPath = 'gitlab-org/gitlab-test';
describe('Deploy Board', () => {
let wrapper;
......@@ -13,7 +15,8 @@ describe('Deploy Board', () => {
deployBoardData: deployBoardMockData,
isLoading: false,
isEmpty: false,
logsPath: environment.log_path,
projectPath,
environmentName: environment.name,
...props,
},
sync: false,
......@@ -60,7 +63,8 @@ describe('Deploy Board', () => {
deployBoardData: {},
isLoading: false,
isEmpty: true,
logsPath: environment.log_path,
projectPath,
environmentName: environment.name,
});
wrapper.vm.$nextTick(done);
});
......@@ -79,7 +83,8 @@ describe('Deploy Board', () => {
deployBoardData: {},
isLoading: true,
isEmpty: false,
logsPath: environment.log_path,
projectPath,
environmentName: environment.name,
});
wrapper.vm.$nextTick(done);
});
......@@ -94,7 +99,8 @@ describe('Deploy Board', () => {
wrapper = createComponent({
isLoading: false,
isEmpty: false,
logsPath: environment.log_path,
projectPath,
environmentName: environment.name,
hasLegacyAppLabel: true,
deployBoardData: {},
});
......
......@@ -12,7 +12,7 @@ export const environmentsList = [
stop_path: '/root/review-app/environments/7/stop',
created_at: '2017-01-31T10:53:46.894Z',
updated_at: '2017-01-31T10:53:46.894Z',
log_path: '/root/review-app/environments/7/logs',
project_path: '/root/review-app',
rollout_status: {},
},
{
......@@ -29,7 +29,7 @@ export const environmentsList = [
stop_path: '/root/review-app/environments/12/stop',
created_at: '2017-02-01T19:42:18.400Z',
updated_at: '2017-02-01T19:42:18.400Z',
log_path: '/root/review-app/environments/12/logs',
project_path: '/root/review-app',
rollout_status: {},
},
];
......@@ -140,5 +140,5 @@ export const folder = {
created_at: '2017-02-01T19:42:18.400Z',
updated_at: '2017-02-01T19:42:18.400Z',
rollout_status: {},
log_path: '/root/review-app/environments/12/logs',
project_path: '/root/review-app',
};
......@@ -23,7 +23,7 @@ describe('Deploy Board Instance', () => {
it('should render a div with the correct css status and tooltip data', () => {
wrapper = createComponent({
logsPath: folder.log_path,
projectPath: folder.project_path,
tooltipText: 'This is a pod',
});
......@@ -46,12 +46,13 @@ describe('Deploy Board Instance', () => {
it('should have a log path computed with a pod name as a parameter', () => {
wrapper = createComponent({
logsPath: folder.log_path,
projectPath: folder.project_path,
environmentName: 'foo',
podName: 'tanuki-1',
});
expect(wrapper.vm.computedLogPath).toEqual(
'/root/review-app/environments/12/logs?pod_name=tanuki-1',
'/root/review-app/-/logs?environment_name=foo&pod_name=tanuki-1',
);
});
});
......@@ -78,10 +79,10 @@ describe('Deploy Board Instance', () => {
wrapper.destroy();
});
it('should not be a link without a logsPath prop', done => {
it('should not be a link without a projectPath prop', done => {
wrapper = createComponent({
stable: false,
logsPath: '',
projectPath: '',
});
wrapper.vm.$nextTick(() => {
......
......@@ -23,7 +23,7 @@ describe Gitlab::EtagCaching::Router do
context 'k8s pod logs' do
it 'matches with pod_name and container_name' do
result = described_class.match(
'/environments/7/pods/pod_name/containers/container_name/logs.json'
'/environments/7/pods/pod_name/containers/container_name/logs/k8s.json'
)
expect(result).to be_present
......@@ -32,7 +32,7 @@ describe Gitlab::EtagCaching::Router do
it 'matches with pod_name' do
result = described_class.match(
'/environments/7/pods/pod_name/containers/logs.json'
'/environments/7/pods/pod_name/containers/logs/k8s.json'
)
expect(result).to be_present
......@@ -41,7 +41,7 @@ describe Gitlab::EtagCaching::Router do
it 'matches without pod_name and container_name' do
result = described_class.match(
'/environments/7/pods/containers/logs.json'
'/environments/7/pods/containers/logs/k8s.json'
)
expect(result).to be_present
......
......@@ -317,11 +317,11 @@ describe Clusters::Platforms::Kubernetes do
expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
expect(store).to receive(:touch)
.with(
::Gitlab::Routing.url_helpers.k8s_pod_logs_project_environment_path(
::Gitlab::Routing.url_helpers.k8s_project_logs_path(
environment.project,
environment,
opts['pod_name'],
opts['container_name'],
environment_name: environment.name,
pod_name: opts['pod_name'],
container_name: opts['container_name'],
format: :json
)
)
......
......@@ -106,7 +106,7 @@ describe 'layouts/nav/sidebar/_project' do
let(:can_read_pod_logs) { true }
it 'link is visible ' do
expect(rendered).to have_link('Pod logs', href: logs_project_environments_path(project))
expect(rendered).to have_link('Pod logs', href: project_logs_path(project))
end
end
......
......@@ -26,6 +26,7 @@
"stop_path": { "type": "string" },
"cancel_auto_stop_path": { "type": "string" },
"folder_path": { "type": "string" },
"project_path": { "type": "string" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"auto_stop_at": { "type": "string", "format": "date-time" },
......
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