[Logstash Centralized Management] Hide recently-deleted pipelines (#18644)

* Extracting constant for reuse

* Keep track of recently-deleted pipelines until Monitoring stops reporting them

* Adding explanatory comment for constant
This commit is contained in:
Shaunak Kashyap 2018-04-30 12:04:39 -07:00
parent c4476243d5
commit f0e30997fd
7 changed files with 110 additions and 10 deletions

View file

@ -12,3 +12,4 @@ export { PLUGIN } from './plugin';
export { ES_SCROLL_SETTINGS } from './es_scroll_settings';
export { TOOLTIPS } from './tooltips';
export { PIPELINE } from './pipeline';
export { MONITORING } from './monitoring';

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
export const MONITORING = {
/**
* How far back in time (from now) do we look in Monitoring data for pipelines to be considered as "currently" running. This is
* also, deliberately, the same duration we give Monitoring to pick up and report on a recently-deleted pipeline before we
* are safe to stop tracking that pipeline as recently-deleted.
*/
PIPELINE_RECENCY_DURATION_S: 30
};

View file

@ -6,7 +6,10 @@
import moment from 'moment';
import chrome from 'ui/chrome';
import { ROUTES } from '../../../common/constants';
import {
ROUTES,
MONITORING
} from '../../../common/constants';
import { PipelineListItem } from 'plugins/logstash/models/pipeline_list_item';
export class MonitoringService {
@ -34,7 +37,7 @@ export class MonitoringService {
const body = {
timeRange: {
max: now.toISOString(),
min: now.subtract(30, 'seconds').toISOString()
min: now.subtract(MONITORING.PIPELINE_RECENCY_DURATION_S, 'seconds').toISOString()
}
};
return this.$http.post(url, body);

View file

@ -10,5 +10,6 @@ import { PipelineService } from './pipeline_service';
uiModules.get('xpack/logstash')
.factory('pipelineService', ($injector) => {
const $http = $injector.get('$http');
return new PipelineService($http);
const pipelinesService = $injector.get('pipelinesService');
return new PipelineService($http, pipelinesService);
});

View file

@ -9,8 +9,9 @@ import { ROUTES } from '../../../common/constants';
import { Pipeline } from 'plugins/logstash/models/pipeline';
export class PipelineService {
constructor($http) {
constructor($http, pipelinesService) {
this.$http = $http;
this.pipelinesService = pipelinesService;
this.basePath = chrome.addBasePath(ROUTES.API_ROOT);
}
@ -30,6 +31,7 @@ export class PipelineService {
deletePipeline(id) {
return this.$http.delete(`${this.basePath}/pipeline/${id}`)
.then(() => this.pipelinesService.addToRecentlyDeleted(id))
.catch(e => {
throw e.data.message;
});

View file

@ -11,7 +11,8 @@ import '../monitoring';
uiModules.get('xpack/logstash')
.factory('pipelinesService', ($injector) => {
const $http = $injector.get('$http');
const $window = $injector.get('$window');
const Promise = $injector.get('Promise');
const monitoringService = $injector.get('xpackLogstashMonitoringService');
return new PipelinesService($http, Promise, monitoringService);
return new PipelinesService($http, $window, Promise, monitoringService);
});

View file

@ -5,12 +5,18 @@
*/
import chrome from 'ui/chrome';
import { ROUTES } from '../../../common/constants';
import {
ROUTES,
MONITORING
} from '../../../common/constants';
import { PipelineListItem } from 'plugins/logstash/models/pipeline_list_item';
const RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY = 'xpack.logstash.recentlyDeletedPipelines';
export class PipelinesService {
constructor($http, Promise, monitoringService) {
constructor($http, $window, Promise, monitoringService) {
this.$http = $http;
this.$window = $window;
this.Promise = Promise;
this.monitoringService = monitoringService;
this.basePath = chrome.addBasePath(ROUTES.API_ROOT);
@ -22,11 +28,38 @@ export class PipelinesService {
this.getMonitoringPipelineList()
])
.then(([managementPipelines, monitoringPipelines]) => {
const now = Date.now();
// Monitoring will report centrally-managed pipelines as well, including recently-deleted centrally-managed ones.
// If there's a recently-deleted pipeline we're keeping track of BUT monitoring doesn't report it, that means
// it's no longer actually running in Logstash any more. So we can stop tracking it as a recently-deleted pipeline.
const monitoringPipelineIds = monitoringPipelines.map(pipeline => pipeline.id);
this.getRecentlyDeleted().forEach(recentlyDeletedPipeline => {
// We don't want to stop tracking the recently-deleted pipeline until Monitoring has had some
// time to report on it. Otherwise, if we stop tracking first, *then* Monitoring reports it, we'll
// still end up showing it in the list until Monitoring stops reporting it.
if (now - recentlyDeletedPipeline.deletedOn < (MONITORING.PIPELINE_RECENCY_DURATION_S * 1000)) {
return;
}
// If Monitoring is still reporting the pipeline, don't stop tracking it yet
if (monitoringPipelineIds.includes(recentlyDeletedPipeline.id)) {
return;
}
this.removeFromRecentlyDeleted(recentlyDeletedPipeline.id);
});
// Merge centrally-managed pipelines with pipelines reported by monitoring. Take care to dedupe
// while merging because monitoring will (rightly) report centrally-managed pipelines as well!
// while merging because monitoring will (rightly) report centrally-managed pipelines as well,
// including recently-deleted ones!
const managementPipelineIds = managementPipelines.map(pipeline => pipeline.id);
return managementPipelines.concat(
monitoringPipelines.filter(monitoringPipeline => !managementPipelineIds.includes(monitoringPipeline.id))
monitoringPipelines
.filter(monitoringPipeline =>
!managementPipelineIds.includes(monitoringPipeline.id)
&& !this.isRecentlyDeleted(monitoringPipeline.id)
)
);
});
}
@ -55,6 +88,50 @@ export class PipelinesService {
headers: { 'Content-Type': 'application/json' }
};
return this.$http.delete(`${this.basePath}/pipelines`, requestOpts)
.then(response => response.data.results);
.then(response => {
this.addToRecentlyDeleted(...pipelineIds);
return response.data.results;
});
}
addToRecentlyDeleted(...pipelineIds) {
const recentlyDeletedPipelines = this.getRecentlyDeleted();
const recentlyDeletedPipelineIds = recentlyDeletedPipelines.map(pipeline => pipeline.id);
pipelineIds.forEach(pipelineId => {
if (!recentlyDeletedPipelineIds.includes(pipelineId)) {
recentlyDeletedPipelines.push({
id: pipelineId,
deletedOn: Date.now()
});
}
});
this.setRecentlyDeleted(recentlyDeletedPipelines);
}
removeFromRecentlyDeleted(...pipelineIds) {
const recentlyDeletedPipelinesToKeep = this.getRecentlyDeleted()
.filter(recentlyDeletedPipeline => !pipelineIds.includes(recentlyDeletedPipeline.id));
this.setRecentlyDeleted(recentlyDeletedPipelinesToKeep);
}
isRecentlyDeleted(pipelineId) {
return this.getRecentlyDeleted()
.map(pipeline => pipeline.id)
.includes(pipelineId);
}
getRecentlyDeleted() {
const recentlyDeletedPipelines = this.$window.localStorage.getItem(RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY);
if (!recentlyDeletedPipelines) {
return [];
}
return JSON.parse(recentlyDeletedPipelines);
}
setRecentlyDeleted(recentlyDeletedPipelineIds) {
this.$window.localStorage.setItem(RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY, JSON.stringify(recentlyDeletedPipelineIds));
}
}