Skip to content
Snippets Groups Projects
Commit b4b9b385 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 2e31c85a
No related branches found
No related tags found
No related merge requests found
Showing
with 599 additions and 107 deletions
Loading
Loading
@@ -5,15 +5,17 @@ import {
GlSprintf,
GlAlert,
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlFormGroup,
GlSearchBoxByClick,
GlInfiniteScroll,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import LogControlButtons from './log_control_buttons.vue';
 
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
import { timeRangeFromUrl } from '~/monitoring/utils';
import { formatDate } from '../utils';
 
Loading
Loading
@@ -22,6 +24,7 @@ export default {
GlSprintf,
GlAlert,
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlFormGroup,
GlSearchBoxByClick,
Loading
Loading
@@ -90,6 +93,16 @@ export default {
shouldShowElasticStackCallout() {
return !this.isElasticStackCalloutDismissed && this.disableAdvancedControls;
},
podDropdownText() {
if (this.pods.current) {
return this.pods.current;
} else if (this.advancedFeaturesEnabled) {
// "All pods" is a valid option when advanced querying is available
return s__('Environments|All pods');
}
return s__('Environments|No pod selected');
},
},
mounted() {
this.setInitData({
Loading
Loading
@@ -178,11 +191,17 @@ export default {
>
<gl-dropdown
id="pods-dropdown"
:text="pods.current || s__('Environments|No pods to display')"
:text="podDropdownText"
:disabled="environments.isLoading"
class="d-flex gl-h-32 js-pods-dropdown"
toggle-class="dropdown-menu-toggle"
>
<template v-if="advancedFeaturesEnabled">
<gl-dropdown-item key="all-pods" @click="showPodLogs(null)">
{{ s__('Environments|All pods') }}
</gl-dropdown-item>
<gl-dropdown-divider />
</template>
<gl-dropdown-item
v-for="podName in pods.options"
:key="podName"
Loading
Loading
Loading
Loading
@@ -82,7 +82,6 @@ export const setTimeRange = ({ dispatch, commit }, timeRange) => {
 
export const showEnvironment = ({ dispatch, commit }, environmentName) => {
commit(types.SET_PROJECT_ENVIRONMENT, environmentName);
commit(types.SET_CURRENT_POD_NAME, null);
dispatch('fetchLogs');
};
 
Loading
Loading
@@ -107,16 +106,16 @@ export const fetchEnvironments = ({ commit, dispatch }, environmentsPath) => {
};
 
export const fetchLogs = ({ commit, state }) => {
commit(types.REQUEST_PODS_DATA);
commit(types.REQUEST_LOGS_DATA);
 
return requestLogsUntilData(state)
.then(({ data }) => {
const { pod_name, pods, logs, cursor } = data;
commit(types.RECEIVE_LOGS_DATA_SUCCESS, { logs, cursor });
commit(types.SET_CURRENT_POD_NAME, pod_name);
 
commit(types.RECEIVE_PODS_DATA_SUCCESS, pods);
commit(types.RECEIVE_LOGS_DATA_SUCCESS, { logs, cursor });
})
.catch(() => {
commit(types.RECEIVE_PODS_DATA_ERROR);
Loading
Loading
import { formatDate } from '../utils';
 
const mapTrace = ({ timestamp = null, message = '' }) =>
[timestamp ? formatDate(timestamp) : '', message].join(' | ');
const mapTrace = ({ timestamp = null, pod = '', message = '' }) =>
[timestamp ? formatDate(timestamp) : '', pod, message].join(' | ');
 
export const trace = state => state.logs.lines.map(mapTrace).join('\n');
 
Loading
Loading
Loading
Loading
@@ -14,6 +14,5 @@ export const REQUEST_LOGS_DATA_PREPEND = 'REQUEST_LOGS_DATA_PREPEND';
export const RECEIVE_LOGS_DATA_PREPEND_SUCCESS = 'RECEIVE_LOGS_DATA_PREPEND_SUCCESS';
export const RECEIVE_LOGS_DATA_PREPEND_ERROR = 'RECEIVE_LOGS_DATA_PREPEND_ERROR';
 
export const REQUEST_PODS_DATA = 'REQUEST_PODS_DATA';
export const RECEIVE_PODS_DATA_SUCCESS = 'RECEIVE_PODS_DATA_SUCCESS';
export const RECEIVE_PODS_DATA_ERROR = 'RECEIVE_PODS_DATA_ERROR';
import * as types from './mutation_types';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
 
const mapLine = ({ timestamp, message }) => ({
const mapLine = ({ timestamp, pod, message }) => ({
timestamp,
pod,
message,
});
 
Loading
Loading
@@ -21,6 +22,10 @@ export default {
// Environments Data
[types.SET_PROJECT_ENVIRONMENT](state, environmentName) {
state.environments.current = environmentName;
// Clear current pod options
state.pods.current = null;
state.pods.options = [];
},
[types.REQUEST_ENVIRONMENTS_DATA](state) {
state.environments.options = [];
Loading
Loading
@@ -81,9 +86,6 @@ export default {
[types.SET_CURRENT_POD_NAME](state, podName) {
state.pods.current = podName;
},
[types.REQUEST_PODS_DATA](state) {
state.pods.options = [];
},
[types.RECEIVE_PODS_DATA_SUCCESS](state, podOptions) {
state.pods.options = podOptions;
},
Loading
Loading
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
 
export default () => ({
Loading
Loading
Loading
Loading
@@ -31,7 +31,8 @@ import DashboardsDropdown from './dashboards_dropdown.vue';
 
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getAddMetricTrackingOptions, timeRangeToUrl, timeRangeFromUrl } from '../utils';
import { defaultTimeRange, timeRanges, metricStates } from '../constants';
import { metricStates } from '../constants';
import { defaultTimeRange, timeRanges } from '~/vue_shared/constants';
 
export default {
components: {
Loading
Loading
Loading
Loading
@@ -3,7 +3,8 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { timeRangeFromUrl, removeTimeRangeParams } from '../utils';
import { sidebarAnimationDuration, defaultTimeRange } from '../constants';
import { sidebarAnimationDuration } from '../constants';
import { defaultTimeRange } from '~/vue_shared/constants';
 
let sidebarMutationObserver;
 
Loading
Loading
import { __ } from '~/locale';
export const PROMETHEUS_TIMEOUT = 120000; // TWO_MINUTES
 
/**
Loading
Loading
@@ -89,37 +87,3 @@ export const dateFormats = {
timeOfDay: 'h:MM TT',
default: 'dd mmm yyyy, h:MMTT',
};
export const timeRanges = [
{
label: __('30 minutes'),
duration: { seconds: 60 * 30 },
},
{
label: __('3 hours'),
duration: { seconds: 60 * 60 * 3 },
},
{
label: __('8 hours'),
duration: { seconds: 60 * 60 * 8 },
default: true,
},
{
label: __('1 day'),
duration: { seconds: 60 * 60 * 24 * 1 },
},
{
label: __('3 days'),
duration: { seconds: 60 * 60 * 24 * 3 },
},
{
label: __('1 week'),
duration: { seconds: 60 * 60 * 24 * 7 * 1 },
},
{
label: __('1 month'),
duration: { seconds: 60 * 60 * 24 * 30 },
},
];
export const defaultTimeRange = timeRanges.find(tr => tr.default);
Loading
Loading
@@ -43,6 +43,11 @@ export default {
required: false,
default: () => defaultTimeRanges,
},
customEnabled: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
Loading
Loading
@@ -166,6 +171,7 @@ export default {
>
<div class="d-flex justify-content-between gl-p-2">
<gl-form-group
v-if="customEnabled"
:label="__('Custom range')"
label-for="custom-from-time"
label-class="gl-pb-1"
Loading
Loading
import { __ } from '~/locale';
const INTERVALS = {
minute: 'minute',
hour: 'hour',
day: 'day',
};
export const timeRanges = [
{
label: __('30 minutes'),
duration: { seconds: 60 * 30 },
name: 'thirtyMinutes',
interval: INTERVALS.minute,
},
{
label: __('3 hours'),
duration: { seconds: 60 * 60 * 3 },
name: 'threeHours',
interval: INTERVALS.hour,
},
{
label: __('8 hours'),
duration: { seconds: 60 * 60 * 8 },
name: 'eightHours',
default: true,
interval: INTERVALS.hour,
},
{
label: __('1 day'),
duration: { seconds: 60 * 60 * 24 * 1 },
name: 'oneDay',
interval: INTERVALS.hour,
},
{
label: __('3 days'),
duration: { seconds: 60 * 60 * 24 * 3 },
name: 'threeDays',
interval: INTERVALS.hour,
},
{
label: __('1 week'),
duration: { seconds: 60 * 60 * 24 * 7 * 1 },
name: 'oneWeek',
interval: INTERVALS.day,
},
{
label: __('1 month'),
duration: { seconds: 60 * 60 * 24 * 30 },
name: 'oneMonth',
interval: INTERVALS.day,
},
];
export const defaultTimeRange = timeRanges.find(tr => tr.default);
export const getTimeWindow = timeWindowName => timeRanges.find(tr => tr.name === timeWindowName);
Loading
Loading
@@ -55,22 +55,10 @@ module PodLogs
return error(_('Cluster does not exist')) if cluster.nil?
return error(_('Namespace is empty')) if namespace.blank?
 
success(result)
end
def check_param_lengths(_result)
pod_name = params['pod_name'].presence
container_name = params['container_name'].presence
result[:pod_name] = params['pod_name'].presence
result[:container_name] = params['container_name'].presence
 
if pod_name&.length.to_i > K8S_NAME_MAX_LENGTH
return error(_('pod_name cannot be larger than %{max_length}'\
' chars' % { max_length: K8S_NAME_MAX_LENGTH }))
elsif container_name&.length.to_i > K8S_NAME_MAX_LENGTH
return error(_('container_name cannot be larger than'\
' %{max_length} chars' % { max_length: K8S_NAME_MAX_LENGTH }))
end
success(pod_name: pod_name, container_name: container_name)
success(result)
end
 
def get_raw_pods(result)
Loading
Loading
@@ -85,40 +73,6 @@ module PodLogs
success(result)
end
 
def check_pod_name(result)
# If pod_name is not received as parameter, get the pod logs of the first
# pod of this namespace.
result[:pod_name] ||= result[:pods].first
unless result[:pod_name]
return error(_('No pods available'))
end
unless result[:pods].include?(result[:pod_name])
return error(_('Pod does not exist'))
end
success(result)
end
def check_container_name(result)
pod_details = result[:raw_pods].first { |p| p.metadata.name == result[:pod_name] }
containers = pod_details.spec.containers.map(&:name)
# select first container if not specified
result[:container_name] ||= containers.first
unless result[:container_name]
return error(_('No containers available'))
end
unless containers.include?(result[:container_name])
return error(_('Container does not exist'))
end
success(result)
end
def pod_logs(result)
raise NotImplementedError
end
Loading
Loading
Loading
Loading
@@ -3,11 +3,8 @@
module PodLogs
class ElasticsearchService < PodLogs::BaseService
steps :check_arguments,
:check_param_lengths,
:get_raw_pods,
:get_pod_names,
:check_pod_name,
:check_container_name,
:check_times,
:check_search,
:check_cursor,
Loading
Loading
@@ -53,7 +50,7 @@ module PodLogs
 
response = ::Gitlab::Elasticsearch::Logs.new(client).pod_logs(
namespace,
result[:pod_name],
pod_name: result[:pod_name],
container_name: result[:container_name],
search: result[:search],
start_time: result[:start],
Loading
Loading
Loading
Loading
@@ -8,7 +8,6 @@ module PodLogs
EncodingHelperError = Class.new(StandardError)
 
steps :check_arguments,
:check_param_lengths,
:get_raw_pods,
:get_pod_names,
:check_pod_name,
Loading
Loading
@@ -22,6 +21,50 @@ module PodLogs
 
private
 
def check_pod_name(result)
# If pod_name is not received as parameter, get the pod logs of the first
# pod of this namespace.
result[:pod_name] ||= result[:pods].first
unless result[:pod_name]
return error(_('No pods available'))
end
unless result[:pod_name].length.to_i <= K8S_NAME_MAX_LENGTH
return error(_('pod_name cannot be larger than %{max_length}'\
' chars' % { max_length: K8S_NAME_MAX_LENGTH }))
end
unless result[:pods].include?(result[:pod_name])
return error(_('Pod does not exist'))
end
success(result)
end
def check_container_name(result)
pod_details = result[:raw_pods].first { |p| p.metadata.name == result[:pod_name] }
containers = pod_details.spec.containers.map(&:name)
# select first container if not specified
result[:container_name] ||= containers.first
unless result[:container_name]
return error(_('No containers available'))
end
unless result[:container_name].length.to_i <= K8S_NAME_MAX_LENGTH
return error(_('container_name cannot be larger than'\
' %{max_length} chars' % { max_length: K8S_NAME_MAX_LENGTH }))
end
unless containers.include?(result[:container_name])
return error(_('Container does not exist'))
end
success(result)
end
def pod_logs(result)
result[:logs] = cluster.kubeclient.get_pod_log(
result[:pod_name],
Loading
Loading
@@ -62,7 +105,8 @@ module PodLogs
values = line.split(' ', 2)
{
timestamp: values[0],
message: values[1]
message: values[1],
pod: result[:pod_name]
}
end
 
Loading
Loading
---
title: Add all pods view to logs explorer
merge_request: 26883
author:
type: added
---
title: Adds crossplane as CI/CD Managed App
merge_request: 27374
author:
type: added
Loading
Loading
@@ -6000,6 +6000,76 @@ type Project {
"""
requestAccessEnabled: Boolean
 
"""
Find a single requirement. Available only when feature flag `requirements_management` is enabled.
"""
requirement(
"""
IID of the requirement, e.g., "1"
"""
iid: ID
"""
List of IIDs of requirements, e.g., [1, 2]
"""
iids: [ID!]
"""
List requirements by sort order
"""
sort: Sort
"""
Filter requirements by state
"""
state: RequirementState
): Requirement
"""
Find requirements. Available only when feature flag `requirements_management` is enabled.
"""
requirements(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
IID of the requirement, e.g., "1"
"""
iid: ID
"""
List of IIDs of requirements, e.g., [1, 2]
"""
iids: [ID!]
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
List requirements by sort order
"""
sort: Sort
"""
Filter requirements by state
"""
state: RequirementState
): RequirementConnection
"""
Detailed version of a Sentry error on the project
"""
Loading
Loading
@@ -6664,6 +6734,41 @@ type Requirement {
userPermissions: RequirementPermissions!
}
 
"""
The connection type for Requirement.
"""
type RequirementConnection {
"""
A list of edges.
"""
edges: [RequirementEdge]
"""
A list of nodes.
"""
nodes: [Requirement]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type RequirementEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: Requirement
}
"""
Check permissions for the current user on a requirement
"""
Loading
Loading
@@ -7463,6 +7568,31 @@ type SnippetPermissions {
updateSnippet: Boolean!
}
 
"""
Common sort values
"""
enum Sort {
"""
Created at ascending order
"""
created_asc
"""
Created at descending order
"""
created_desc
"""
Updated at ascending order
"""
updated_asc
"""
Updated at descending order
"""
updated_desc
}
type Submodule implements Entry {
"""
Flat path of the entry
Loading
Loading
Loading
Loading
@@ -18032,6 +18032,168 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "requirement",
"description": "Find a single requirement. Available only when feature flag `requirements_management` is enabled.",
"args": [
{
"name": "iid",
"description": "IID of the requirement, e.g., \"1\"",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{
"name": "iids",
"description": "List of IIDs of requirements, e.g., [1, 2]",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "sort",
"description": "List requirements by sort order",
"type": {
"kind": "ENUM",
"name": "Sort",
"ofType": null
},
"defaultValue": null
},
{
"name": "state",
"description": "Filter requirements by state",
"type": {
"kind": "ENUM",
"name": "RequirementState",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "Requirement",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "requirements",
"description": "Find requirements. Available only when feature flag `requirements_management` is enabled.",
"args": [
{
"name": "iid",
"description": "IID of the requirement, e.g., \"1\"",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{
"name": "iids",
"description": "List of IIDs of requirements, e.g., [1, 2]",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "sort",
"description": "List requirements by sort order",
"type": {
"kind": "ENUM",
"name": "Sort",
"ofType": null
},
"defaultValue": null
},
{
"name": "state",
"description": "Filter requirements by state",
"type": {
"kind": "ENUM",
"name": "RequirementState",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "RequirementConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "sentryDetailedError",
"description": "Detailed version of a Sentry error on the project",
Loading
Loading
@@ -20106,6 +20268,118 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "RequirementConnection",
"description": "The connection type for Requirement.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "RequirementEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Requirement",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "RequirementEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Requirement",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "RequirementPermissions",
Loading
Loading
@@ -22643,6 +22917,41 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "Sort",
"description": "Common sort values",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "updated_desc",
"description": "Updated at descending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updated_asc",
"description": "Updated at ascending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "created_desc",
"description": "Created at descending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "created_asc",
"description": "Created at ascending order",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "String",
Loading
Loading
Loading
Loading
@@ -898,6 +898,7 @@ Information about pagination in a connection.
| `removeSourceBranchAfterMerge` | Boolean | Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project |
| `repository` | Repository | Git repository of the project |
| `requestAccessEnabled` | Boolean | Indicates if users can request member access to the project |
| `requirement` | Requirement | Find a single requirement. Available only when feature flag `requirements_management` is enabled. |
| `sentryDetailedError` | SentryDetailedError | Detailed version of a Sentry error on the project |
| `sentryErrors` | SentryErrorCollection | Paginated collection of Sentry errors on the project |
| `serviceDeskAddress` | String | E-mail address of the service desk. |
Loading
Loading
Loading
Loading
@@ -81,7 +81,7 @@ already reserved for category labels).
The descriptions on the [labels page](https://gitlab.com/groups/gitlab-org/-/labels)
explain what falls under each type label.
 
The GitLab handbook documents [when something is a bug and when it is a feature request.](https://about.gitlab.com/handbook/product/product-management/process/feature-or-bug.html)
The GitLab handbook documents [when something is a bug and when it is a feature request](https://about.gitlab.com/handbook/product/product-management/process/feature-or-bug.html).
 
### Facet labels
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment