Merge pull request #14986 from hashicorp/ui/feature/filter-node-healthchecks-agentless

UI: filter node healthchecks on agentless service instances
This commit is contained in:
Tyler Wendlandt 2022-10-14 09:33:45 -06:00 committed by GitHub
commit 0c8563f060
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 191 additions and 97 deletions

3
.changelog/14986.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ui: Filter out node health checks on agentless service instances
```

View File

@ -1,11 +1,12 @@
export default (collection, text) => export default (collection, text) =>
(scope = '.consul-health-check-list') => { (scope = '.consul-health-check-list') => {
return { return collection({
scope, scope,
item: collection('li', { itemScope: 'li',
name: text('header h3'), item: {
name: text('header h2'),
type: text('[data-health-check-type]'), type: text('[data-health-check-type]'),
exposed: text('[data-test-exposed]'), exposed: text('[data-test-exposed]'),
}), },
}; });
}; };

View File

@ -0,0 +1,14 @@
import Controller from '@ember/controller';
import { action } from '@ember/object';
export default class HealthChecksController extends Controller {
@action
syntheticNodeSearchPropertyFilter(item, searchProperty) {
return !(item.Node.Meta?.['synthetic-node'] && searchProperty === 'Node');
}
@action
syntheticNodeHealthCheckFilter(item, healthcheck, index, list) {
return !(item.Node.Meta?.['synthetic-node'] && healthcheck?.Kind === 'node');
}
}

View File

@ -1,93 +1,90 @@
<Route <Route @name={{routeName}} as |route|>
@name={{routeName}}
as |route|>
{{#let {{#let
(filter (action 'syntheticNodeSearchPropertyFilter' route.model.item) searchProperties)
(hash as |filteredSearchProperties|
value=(or sortBy "Status:asc") }}
change=(action (mut sortBy) value="target.selected") {{#let
) (hash value=(or sortBy 'Status:asc') change=(action (mut sortBy) value='target.selected'))
(hash
(hash status=(hash
status=(hash value=(if status (split status ',') undefined)
value=(if status (split status ',') undefined) change=(action (mut status) value='target.selectedItems')
change=(action (mut status) value="target.selectedItems") )
) check=(hash
check=(hash value=(if check (split check ',') undefined)
value=(if check (split check ',') undefined) change=(action (mut check) value='target.selectedItems')
change=(action (mut check) value="target.selectedItems") )
) searchproperty=(hash
searchproperty=(hash value=(if
value=(if (not-eq searchproperty undefined) (not-eq searchproperty undefined) (split searchproperty ',') filteredSearchProperties
(split searchproperty ',') )
searchProperties change=(action (mut searchproperty) value='target.selectedItems')
default=filteredSearchProperties
) )
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
) )
) (filter
(action 'syntheticNodeHealthCheckFilter' route.model.item)
(merge-checks
(array route.model.item.Checks route.model.proxy.Checks)
route.model.proxy.ServiceProxy.Expose.Checks
)
)
as |sort filters items|
}}
<div class='tab-section'>
(merge-checks (array route.model.item.Checks route.model.proxy.Checks) route.model.proxy.ServiceProxy.Expose.Checks) {{#if (gt items.length 0)}}
<input type='checkbox' id='toolbar-toggle' />
as |sort filters items|}} <Consul::HealthCheck::SearchBar
<div class="tab-section"> @search={{search}}
@onsearch={{action (mut search) value='target.value'}}
{{#if (gt items.length 0) }} @sort={{sort}}
<input type="checkbox" id="toolbar-toggle" /> @filter={{filters}}
<Consul::HealthCheck::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@filter={{filters}}
/>
{{/if}}
{{#let (find-by "Type" "serf" items) as |serf|}}
{{#if (and serf (eq serf.Status "critical"))}}
<Notice
data-test-critical-serf-notice
@type="warning"
as |notice|>
<notice.Header>
<h2>
{{t "routes.dc.services.instance.healthchecks.critical-serf-notice.header"}}
</h2>
</notice.Header>
<notice.Body>
{{t
"routes.dc.services.instance.healthchecks.critical-serf-notice.body"
htmlSafe=true
}}
</notice.Body>
</Notice>
{{/if}}
{{/let}}
<DataCollection
@type="health-check"
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::HealthCheck::List
@items={{collection.items}}
/> />
</collection.Collection> {{/if}}
<collection.Empty>
<EmptyState> {{#let (find-by 'Type' 'serf' items) as |serf|}}
<BlockSlot @name="body"> {{#if (and serf (eq serf.Status 'critical'))}}
{{t "routes.dc.services.instance.healthchecks.empty" <Notice data-test-critical-serf-notice @type='warning' as |notice|>
items=items.length <notice.Header>
htmlSafe=true <h2>
}} {{t 'routes.dc.services.instance.healthchecks.critical-serf-notice.header'}}
</BlockSlot> </h2>
</EmptyState> </notice.Header>
<notice.Body>
{{t
'routes.dc.services.instance.healthchecks.critical-serf-notice.body'
htmlSafe=true
}}
</notice.Body>
</Notice>
{{/if}}
{{/let}}
<DataCollection
@type='health-check'
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|
>
<collection.Collection>
<Consul::HealthCheck::List @items={{collection.items}} />
</collection.Collection>
<collection.Empty>
<EmptyState>
<BlockSlot @name='body'>
{{t
'routes.dc.services.instance.healthchecks.empty'
items=items.length
htmlSafe=true
}}
</BlockSlot>
</EmptyState>
</collection.Empty> </collection.Empty>
</DataCollection> </DataCollection>
</div> </div>
{{/let}}
{{/let}} {{/let}}
</Route> </Route>

View File

@ -64,3 +64,68 @@ Feature: dc / services / instances / health-checks
Then the url should be /dc1/services/service-0/instances/another-node/service-1-with-id/health-checks Then the url should be /dc1/services/service-0/instances/another-node/service-1-with-id/health-checks
And I see healthChecksIsSelected on the tabs And I see healthChecksIsSelected on the tabs
And I don't see criticalSerfNotice on the tabs.healthChecksTab And I don't see criticalSerfNotice on the tabs.healthChecksTab
Scenario: Node health check should be hidden on agentless service instances
Given 1 instance model from yaml
---
- Service:
ID: service-0-with-id
Node:
Node: another-node
Meta:
synthetic-node: true
Checks:
- Type: ''
Name: Node Health Check
CheckID: serfHealth
ServiceID: ''
Status: passing
Output: Agent alive and reachable
- Type: ''
Name: Serf Health Status
CheckID: serfHealth
Status: critical
Output: ouch
---
When I visit the instance page for yaml
---
dc: dc1
service: service-0
node: another-node
id: service-0-with-id
---
Then the url should be /dc1/services/service-0/instances/another-node/service-0-with-id/health-checks
And I see healthChecksIsSelected on the tabs
And I see 1 healthCheck model on the tabs.healthChecksTab component
And I see 1 healthCheck model with the name "Serf Health Status"
Scenario: Node health checks should be visible on non-agentless service instances
Given 1 instance model from yaml
---
- Service:
ID: service-0-with-id
Node:
Node: another-node
Checks:
- Type: ''
Name: Node Health Check
CheckID: serfHealth
ServiceID: ''
Status: passing
Output: Agent alive and reachable
- Type: ''
Name: Serf Health Status
CheckID: serfHealth
Status: critical
Output: ouch
---
When I visit the instance page for yaml
---
dc: dc1
service: service-0
node: another-node
id: service-0-with-id
---
Then the url should be /dc1/services/service-0/instances/another-node/service-0-with-id/health-checks
And I see healthChecksIsSelected on the tabs
And I see 2 healthCheck models on the tabs.healthChecksTab component
And I see 1 healthCheck model with the name "Serf Health Status"
And I see 1 healthCheck model with the name "Node Health Check"

View File

@ -75,7 +75,7 @@ Feature: dc / services / instances / show: Show Service Instance
And I don't see upstreams on the tabs And I don't see upstreams on the tabs
And I see healthChecksIsSelected on the tabs And I see healthChecksIsSelected on the tabs
And I see 6 of the checks object And I see 6 healthCheck models
When I click tags&Meta on the tabs When I click tags&Meta on the tabs
And I see tags&MetaIsSelected on the tabs And I see tags&MetaIsSelected on the tabs

View File

@ -1,8 +1,25 @@
export default function (scenario, assert, find, currentPage, pauseUntil, pluralize) { export default function (scenario, assert, find, currentPage, pauseUntil, pluralize) {
function getModelItems(model, component) {
let obj;
if (component) {
obj = find(component);
} else {
obj = currentPage();
}
let found = obj[pluralize(model)];
if (typeof found === 'function') {
found = found();
}
return found;
}
scenario scenario
.then('pause until I see $number $model model[s]?', function (num, model) { .then('pause until I see $number $model model[s]?', function (num, model) {
return pauseUntil(function (resolve, reject, retry) { return pauseUntil(function (resolve, reject, retry) {
const len = currentPage()[pluralize(model)].filter(function (item) { const len = getModelItems(model).filter(function (item) {
return item.isVisible; return item.isVisible;
}).length; }).length;
if (len === num) { if (len === num) {
@ -15,8 +32,7 @@ export default function (scenario, assert, find, currentPage, pauseUntil, plural
'pause until I see $number $model model[s]? on the $component component', 'pause until I see $number $model model[s]? on the $component component',
function (num, model, component) { function (num, model, component) {
return pauseUntil(function (resolve, reject, retry) { return pauseUntil(function (resolve, reject, retry) {
const obj = find(component); const len = getModelItems(model, component).filter(function (item) {
const len = obj[pluralize(model)].filter(function (item) {
return item.isVisible; return item.isVisible;
}).length; }).length;
if (len === num) { if (len === num) {
@ -27,7 +43,7 @@ export default function (scenario, assert, find, currentPage, pauseUntil, plural
} }
) )
.then(['I see $num $model model[s]?'], function (num, model) { .then(['I see $num $model model[s]?'], function (num, model) {
const len = currentPage()[pluralize(model)].filter(function (item) { const len = getModelItems(model).filter(function (item) {
return item.isVisible; return item.isVisible;
}).length; }).length;
assert.equal(len, num, `Expected ${num} ${pluralize(model)}, saw ${len}`); assert.equal(len, num, `Expected ${num} ${pluralize(model)}, saw ${len}`);
@ -35,15 +51,13 @@ export default function (scenario, assert, find, currentPage, pauseUntil, plural
.then( .then(
['I see $num $model model[s]? on the $component component'], ['I see $num $model model[s]? on the $component component'],
function (num, model, component) { function (num, model, component) {
const obj = find(component); const len = getModelItems(model, component).filter(function (item) {
const len = obj[pluralize(model)].filter(function (item) {
return item.isVisible; return item.isVisible;
}).length; }).length;
assert.equal(len, num, `Expected ${num} ${pluralize(model)}, saw ${len}`); assert.equal(len, num, `Expected ${num} ${pluralize(model)}, saw ${len}`);
} }
) )
// TODO: I${ dont } see
.then( .then(
[`I see $num $model model[s]? with the $property "$value"`], [`I see $num $model model[s]? with the $property "$value"`],
function ( function (
@ -53,7 +67,7 @@ export default function (scenario, assert, find, currentPage, pauseUntil, plural
property, property,
value value
) { ) {
const len = currentPage()[pluralize(model)].filter(function (item) { const len = getModelItems(model).filter(function (item) {
if (item.isVisible) { if (item.isVisible) {
let prop = item[property]; let prop = item[property];
// cope with pageObjects that can have a multiple: true // cope with pageObjects that can have a multiple: true