Merge pull request #1659 from vimniky/alert-logging

Alert: Fixed sorting & searching and filtering bugs.
This commit is contained in:
Vincent Fiduccia 2018-02-25 00:12:19 -07:00 committed by GitHub
commit fceefdd8c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 131 additions and 62 deletions

View File

@ -21,8 +21,8 @@ const headers = [
{ {
translationKey: 'alertPage.index.table.target', translationKey: 'alertPage.index.table.target',
name: 'target', name: 'target',
searchField: ['target', 'displayTargetType'], searchField: ['nodeName', 'resourceKind', 'displayTargetType'],
sort: ['target', 'id'], sort: ['nodeName', 'id', 'resourceKind'],
}, },
{ {
translationKey: 'alertPage.index.table.condition', translationKey: 'alertPage.index.table.condition',
@ -33,14 +33,16 @@ const headers = [
{ {
translationKey: 'alertPage.index.table.recipients', translationKey: 'alertPage.index.table.recipients',
name: 'recipients', name: 'recipients',
searchField: ['recipient'], searchField: ['recipient', 'firstRecipient'],
sort: ['recipients'], sort: ['recipients', 'firstRecipient'],
}, },
]; ];
export default Component.extend({ export default Component.extend({
pageScope: reads('scope.currentPageScope'),
scope: service(), scope: service(),
clusterId: reads('scope.currentCluster.id'),
projectId: reads('scope.currentProject.id'),
pageScope: reads('scope.currentPageScope'),
// input // input
model: null, model: null,
@ -54,7 +56,19 @@ export default Component.extend({
search: true, search: true,
searchText: null, searchText: null,
filteredNotifiers: function() {
const clusterId = get(this, 'clusterId');
return get(this, 'notifiers').filterBy('clusterId', clusterId);
}.property('clusterId', 'notifiers.@each.{clusterId}'),
filteredAlerts: function() { filteredAlerts: function() {
return get(this, 'model'); const clusterId = get(this, 'clusterId');
}.property('model.[]'), const projectId = get(this, 'projectId');
const ps = get(this, 'pageScope');
if (ps === 'cluster') {
return get(this, 'alerts').filterBy('clusterId', clusterId);
} else {
return get(this, 'alerts').filterBy('projectId', projectId);
}
}.property('alerts.@each.{clusterId,projectId}', 'clusterId', 'projectId', 'pageScope'),
}); });

View File

@ -9,14 +9,13 @@
searchText=searchText searchText=searchText
headers=headers headers=headers
body=filteredAlerts body=filteredAlerts
fullRows=fullRows
as |sortable kind row dt| as |sortable kind row dt|
}} }}
{{#if (eq kind "row")}} {{#if (eq kind "row")}}
{{#if (eq pageScope 'cluster')}} {{#if (eq pageScope 'cluster')}}
{{cluster-alert-row notifiers=notifiers model=row}} {{cluster-alert-row notifiers=filteredNotifiers model=row}}
{{else if (eq pageScope 'project')}} {{else if (eq pageScope 'project')}}
{{project-alert-row notifiers=notifiers model=row}} {{project-alert-row notifiers=filteredNotifiers model=row}}
{{else}} {{else}}
<div>Oops, it's impossible</div> <div>Oops, it's impossible</div>
{{/if}} {{/if}}

View File

@ -1,5 +1,7 @@
import { get, set } from '@ember/object'; import { get, set } from '@ember/object';
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { reads } from '@ember/object/computed';
const SYSTEM_SERVICES = [ const SYSTEM_SERVICES = [
{label: 'Etcd', value: 'etcd'}, {label: 'Etcd', value: 'etcd'},
@ -16,6 +18,9 @@ const RESOURCE_KINDS = [
{label: 'Daemonset', value: 'Daemonset'}, {label: 'Daemonset', value: 'Daemonset'},
]; ];
export default Component.extend({ export default Component.extend({
globalStore: service(),
scope: service(),
clusterId: reads('scope.currentCluster.id'),
init(...args) { init(...args) {
this._super(...args); this._super(...args);
@ -23,6 +28,11 @@ export default Component.extend({
this.set('resourceKinds', RESOURCE_KINDS); this.set('resourceKinds', RESOURCE_KINDS);
}, },
nodes: function() {
const clusterId = get(this, 'clusterId');
return get(this, 'globalStore').all('node').filterBy('clusterId', clusterId);
}.property('clusterId'),
isEventTarget: function() { isEventTarget: function() {
const t = get(this, 'model._targetType'); const t = get(this, 'model._targetType');
return t === 'warningEvent' || t === 'normalEvent'; return t === 'warningEvent' || t === 'normalEvent';

View File

@ -50,7 +50,7 @@
</div> </div>
<div class="col span-9 mt-0"> <div class="col span-9 mt-0">
{{searchable-select {{searchable-select
content=resourceMap.nodes content=nodes
class="form-control" class="form-control"
value=model.targetNode.nodeId value=model.targetNode.nodeId
optionValuePath="id" optionValuePath="id"

View File

@ -1,4 +1,6 @@
import { get, set } from '@ember/object'; import { get, set } from '@ember/object';
import { inject as service } from '@ember/service';
import { reads } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
const WORLOAD_TYPES = [ const WORLOAD_TYPES = [
@ -8,6 +10,10 @@ const WORLOAD_TYPES = [
]; ];
export default Component.extend({ export default Component.extend({
globalStore: service(),
scope: service(),
clusterId: reads('scope.currentCluster.id'),
projectId: reads('scope.currentProject.id'),
restartIntervalSeconds: null, restartIntervalSeconds: null,
@ -19,6 +25,26 @@ export default Component.extend({
set(this, 'model.targetPod.restartIntervalSeconds', n * 60); set(this, 'model.targetPod.restartIntervalSeconds', n * 60);
}, },
pods: function() {
const clusterId = get(this, 'clusterId');
return get(this, 'store').all('pod').filterBy('clusterId', clusterId);
}.property('clusterId'),
deployments: function() {
const projectId = get(this, 'projectId');
return get(this, 'store').all('deployment').filterBy('projectId', projectId);
}.property('projectId'),
daemonsets: function() {
const projectId = get(this, 'projectId');
return get(this, 'store').all('daemonset').filterBy('projectId', projectId);
}.property('projectId'),
statefulsets: function() {
const projectId = get(this, 'projectId');
return get(this, 'store').all('statefulset').filterBy('projectId', projectId);
}.property('projectId'),
restartIntervalSecondsChanged: function() { restartIntervalSecondsChanged: function() {
const n = +get(this, 'restartIntervalSeconds') || 5; const n = +get(this, 'restartIntervalSeconds') || 5;
set(this, 'model.targetPod.restartIntervalSeconds', n * 60); set(this, 'model.targetPod.restartIntervalSeconds', n * 60);
@ -27,15 +53,15 @@ export default Component.extend({
workloads: function() { workloads: function() {
const t = get(this, 'model.targetWorkload.workloadType'); const t = get(this, 'model.targetWorkload.workloadType');
if (t === 'deployment') { if (t === 'deployment') {
return get(this, 'resourceMap.deployments'); return get(this, 'deployments');
} }
if (t === 'daemonset') { if (t === 'daemonset') {
return get(this, 'resourceMap.daemonsets'); return get(this, 'daemonsets');
} }
if (t === 'statefulset') { if (t === 'statefulset') {
return get(this, 'resourceMap.statefulsets'); return get(this, 'statefulsets');
} }
}.property('resourceMap.deployments.[]', 'resourceMap.daemonsets.[]', 'resourceMap.statefulsets.[]', 'model.targetWorkload.workloadType'), }.property('deployments.[]', 'daemonsets.[]', 'statefulsets.[]', 'model.targetWorkload.workloadType'),
actions: { actions: {
// todo, don't know that this is needed // todo, don't know that this is needed

View File

@ -14,7 +14,7 @@
</div> </div>
<div class="col span-8 mt-0"> <div class="col span-8 mt-0">
{{searchable-select {{searchable-select
content=resourceMap.pods content=pods
class="form-control" class="form-control"
value=model.targetPod.podId value=model.targetPod.podId
optionValuePath="id" optionValuePath="id"

View File

@ -4,6 +4,7 @@ import { reads } from '@ember/object/computed';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
globalStore: service(),
scope: service(), scope: service(),
clusterId: reads('scope.currentCluster.id'), clusterId: reads('scope.currentCluster.id'),
@ -20,6 +21,11 @@ export default Component.extend({
get(this, 'model.recipients').pushObject(nue); get(this, 'model.recipients').pushObject(nue);
}, },
notifiers: function() {
const clusterId = get(this, 'clusterId');
return get(this, 'globalStore').all('notifier').filterBy('clusterId', clusterId);
}.property('clusterId'),
disableRemove: function() { disableRemove: function() {
return get(this, 'model.recipients.length') <= 1; return get(this, 'model.recipients.length') <= 1;
}.property('model.recipients.length'), }.property('model.recipients.length'),

View File

@ -16,7 +16,6 @@
<div class=""> <div class="">
{{# if (eq pageScope 'cluster')}} {{# if (eq pageScope 'cluster')}}
{{alert/form-cluster-rules {{alert/form-cluster-rules
resourceMap=resourceMap
pageScope=pageScope pageScope=pageScope
isCreate=isCreate isCreate=isCreate
model=newAlert model=newAlert
@ -24,7 +23,6 @@
{{else if (eq pageScope 'project')}} {{else if (eq pageScope 'project')}}
{{alert/form-project-rules {{alert/form-project-rules
pageScope=pageScope pageScope=pageScope
resourceMap=resourceMap
isCreate=isCreate isCreate=isCreate
model=newAlert model=newAlert
}} }}
@ -35,7 +33,6 @@
{{alert/form-recipients {{alert/form-recipients
pageScope=pageScope pageScope=pageScope
isCreate=isCreate isCreate=isCreate
notifiers=resourceMap.notifiers
model=newAlert model=newAlert
}} }}
</div> </div>

View File

@ -11,12 +11,6 @@ export default Component.extend(notifierMixin, {
classNames: 'main-row', classNames: 'main-row',
bulkActions: true, bulkActions: true,
resourceKind: function() {
const rk = get(this, 'model.targetEvent.resourceKind');
return get(this, 'intl').t(`alertPage.resourceKinds.${rk}`);
}.property('model.targetEvent.resourceKind'),
selectorList: function() { selectorList: function() {
const t = get(this, 'model.targetType'); const t = get(this, 'model.targetType');
if (t === 'nodeSelector') { if (t === 'nodeSelector') {

View File

@ -36,7 +36,7 @@
{{/if}} {{/if}}
{{else if (eq model.targetType 'node')}} {{else if (eq model.targetType 'node')}}
<div class="text-muted text-small"> <div class="text-muted text-small">
{{model.targetNode.nodeId}} {{model.nodeName}}
</div> </div>
{{else if (eq model.targetType 'systemService')}} {{else if (eq model.targetType 'systemService')}}
<div class="text-muted text-small"> <div class="text-muted text-small">
@ -44,7 +44,7 @@
</div> </div>
{{else if (eq model.targetType 'event')}} {{else if (eq model.targetType 'event')}}
<div class="text-muted text-small"> <div class="text-muted text-small">
<span class="text-capicalize">{{resourceKind}}</span> {{t 'alertPage.targetTypes.event'}} <span class="text-capicalize">{{model.resourceKind}}</span> {{t 'alertPage.targetTypes.event'}}
</div> </div>
{{/if}} {{/if}}
</td> </td>
@ -58,9 +58,10 @@
{{#if (eq model.recipients.length 1)}} {{#if (eq model.recipients.length 1)}}
<div class="clip"> <div class="clip">
<div class="text-capitalize">{{item.notifierType}}</div> <div class="text-capitalize">{{item.notifierType}}</div>
{{#if firstRecipient}} {{#if model.firstRecipient}}
{{firstRecipient.name}} {{model.firstRecipient}}
{{else}} {{else}}
{{t 'alertPage.na'}}
{{/if}} {{/if}}
</div> </div>
{{else if (gt model.recipients.length 1)}} {{else if (gt model.recipients.length 1)}}

View File

@ -18,13 +18,6 @@ export default Component.extend(notifierMixin, {
return t === 'pod' && c === 'restarts'; return t === 'pod' && c === 'restarts';
}.property('model.targetType', 'model.targetPod.condition'), }.property('model.targetType', 'model.targetPod.condition'),
displayTargetType: function() {
const t = get(this, 'model.targetType');
const intl = get(this, 'intl');
return intl.t(`alertPage.targetTypes.${t}`);
}.property('model.targetType'),
selectorList: function() { selectorList: function() {
const t = get(this, 'model.targetType'); const t = get(this, 'model.targetType');
if (t === 'workloadSelector') { if (t === 'workloadSelector') {

View File

@ -59,8 +59,8 @@
{{#if (eq model.recipients.length 1)}} {{#if (eq model.recipients.length 1)}}
<div class="clip"> <div class="clip">
<div class="text-capitalize">{{item.notifierType}}</div> <div class="text-capitalize">{{item.notifierType}}</div>
{{#if firstRecipient}} {{#if model.firstRecipient}}
{{firstRecipient.name}} {{model.firstRecipient}}
{{else}} {{else}}
{{t 'alertPage.na'}} {{t 'alertPage.na'}}
{{/if}} {{/if}}

View File

@ -15,9 +15,7 @@ export default Route.extend({
const opt = { const opt = {
filter: {clusterId}, filter: {clusterId},
}; };
const notifiers = gs.findAll('notifier', opt).then(() => { const notifiers = gs.findAll('notifier', opt);
return gs.all('notifier');
});
const alerts = gs.findAll('clusterAlert', opt).then(() => { const alerts = gs.findAll('clusterAlert', opt).then(() => {
return gs.all('clusterAlert'); return gs.all('clusterAlert');
}); });
@ -28,9 +26,7 @@ export default Route.extend({
}, },
loadProjectResource({clusterId, projectId}) { loadProjectResource({clusterId, projectId}) {
let gs = get(this, 'globalStore'); let gs = get(this, 'globalStore');
const notifiers = gs.findAll('notifier', {filter: {clusterId}}).then(() => { const notifiers = gs.findAll('notifier', {filter: {clusterId}});
return gs.all('notifier');
});
const alerts = gs.findAll('projectAlert', {filter: {projectId}}).then(() => { const alerts = gs.findAll('projectAlert', {filter: {projectId}}).then(() => {
return gs.all('projectAlert'); return gs.all('projectAlert');
}); });

View File

@ -5,8 +5,4 @@
</div> </div>
</section> </section>
{{alert-table {{alert-table alerts=model.alerts notifiers=model.notifiers}}
pageScope=pageScope
notifiers=notifiers
model=alerts
}}

View File

@ -5,7 +5,9 @@ import { reads } from '@ember/object/computed';
export default Mixin.create({ export default Mixin.create({
router: service(), router: service(),
globalStore: service(),
scope: service(), scope: service(),
intl: service(),
pageScope: reads('scope.currentPageScope'), pageScope: reads('scope.currentPageScope'),
relevantState: function() { relevantState: function() {
@ -22,6 +24,45 @@ export default Mixin.create({
this.constructor.stateMap = stateMap this.constructor.stateMap = stateMap
}, },
displayTargetType: function() {
const t = get(this, 'targetType');
const intl = get(this, 'intl');
return intl.t(`alertPage.targetTypes.${t}`);
}.property('targetType'),
resourceKind: function() {
const rk = get(this, 'targetEvent.resourceKind');
return get(this, 'intl').t(`alertPage.resourceKinds.${rk}`);
}.property('targetEvent.resourceKind'),
firstRecipient: function() {
const recipient = (get(this, 'recipients') || []).get('firstObject');
if (recipient && get(recipient, 'notifierId')) {
const notifierId = get(recipient, 'notifierId');
if (!notifierId) return null;
const notifier = get(this, 'globalStore').all('notifier').filterBy('id', notifierId).get('firstObject');
if (!notifier) {
return null;
}
return notifier.get('displayName');
}
return null;
}.property('recipients.length'),
nodeName: function() {
const id = get(this, 'targetNode.nodeId');
if (!id) {
return null;
}
const node = get(this, 'globalStore').all('node').filterBy('id', id).get('firstObject');
if (!node) {
return null;
}
return node.get('displayName');
}.property('targetNode.nodeId'),
actions: { actions: {
edit() { edit() {
const ps = get(this, 'pageScope'); const ps = get(this, 'pageScope');

View File

@ -11,15 +11,6 @@ export default Mixin.create({
return notifiers.filterBy('id', id).get('firstObject'); return notifiers.filterBy('id', id).get('firstObject');
}, },
firstRecipient: function() {
const recipient = get(this, 'model.recipients').get('firstObject');
if (recipient && get(recipient, 'notifierId')) {
const notifierId = get(recipient, 'notifierId');
return this.getNotifierById(notifierId)
}
return null;
}.property('model.recipients.length'),
recipientsTip: function() { recipientsTip: function() {
const recipients = get(this, 'model.recipients') || []; const recipients = get(this, 'model.recipients') || [];
const out = recipients.map(recipient => { const out = recipients.map(recipient => {

View File

@ -76,7 +76,6 @@ export default Route.extend({
const globalStore = get(this, 'globalStore'); const globalStore = get(this, 'globalStore');
const newAlert = this.getNewProjectAlert(projectId); const newAlert = this.getNewProjectAlert(projectId);
const opt = {filter: {projectId}}; const opt = {filter: {projectId}};
return hash({ return hash({
pods: store.findAll('pod', opt), pods: store.findAll('pod', opt),
statefulsets: store.findAll('statefulset', opt), statefulsets: store.findAll('statefulset', opt),

View File

@ -1,4 +1,7 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { reads } from '@ember/object/computed';
import { get } from '@ember/object';
import layout from './template'; import layout from './template';
const headers = [ const headers = [
@ -30,6 +33,8 @@ const headers = [
]; ];
export default Component.extend({ export default Component.extend({
scope: service(),
clusterId: reads('scope.currentCluster.id'),
layout, layout,
// input // input
model: null, model: null,
@ -37,7 +42,8 @@ export default Component.extend({
headers, headers,
filteredNotifiers: function() { filteredNotifiers: function() {
const notifiers = this.get('model') || []; const data = this.get('model') || [];
return notifiers; const clusterId = get(this, 'clusterId')
}.property('model.[]'), return data.filterBy('clusterId', clusterId);
}.property('model.@each.{clusterId}', 'clusterId'),
}); });