mirror of https://github.com/status-im/consul.git
ui: Expose checks (#6575)
Adds visibility for `Expose.Checks` config setting for proxies. 1. Adds an 'Exposed Path' tab to the proxy detail page to show the user information on exposed paths. 2. If the users has exposed their healthchecks we also add this information to the Service detail page for this proxy (only for http2 and gRPC checks)
This commit is contained in:
parent
5a2fc5bffb
commit
e6ecb47765
|
@ -42,7 +42,14 @@
|
||||||
}
|
}
|
||||||
/* TODO: Try and use the same vertical positioning all tooltips */
|
/* TODO: Try and use the same vertical positioning all tooltips */
|
||||||
/* this is only for pseudo tooltips be want to avoid */
|
/* this is only for pseudo tooltips be want to avoid */
|
||||||
/* specifiying pseudo in this file */
|
/* specifying pseudo in this file */
|
||||||
%tooltip::after {
|
%tooltip::after {
|
||||||
bottom: calc(100% - 7px);
|
bottom: calc(100% - 7px);
|
||||||
}
|
}
|
||||||
|
%tooltip-bottom::before {
|
||||||
|
bottom: auto;
|
||||||
|
top: calc(100% + 7px);
|
||||||
|
}
|
||||||
|
%tooltip-bottom::after {
|
||||||
|
bottom: -12px;
|
||||||
|
}
|
||||||
|
|
|
@ -3,12 +3,21 @@
|
||||||
color: $white;
|
color: $white;
|
||||||
background-color: $gray-500;
|
background-color: $gray-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* borders here are used to draw a triangle in CSS */
|
||||||
|
/* the are not actual borders */
|
||||||
|
|
||||||
%tooltip-tail {
|
%tooltip-tail {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
border-left: 9px solid transparent;
|
border-left: 9px solid transparent;
|
||||||
border-right: 9px solid transparent;
|
border-right: 9px solid transparent;
|
||||||
|
|
||||||
border-top: 18px solid $gray-500;
|
border-top: 18px solid $gray-500;
|
||||||
}
|
}
|
||||||
|
%tooltip-bottom::after {
|
||||||
|
border-top: 0;
|
||||||
|
border-bottom: 18px solid $gray-500;
|
||||||
|
}
|
||||||
%tooltip-bubble {
|
%tooltip-bubble {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
border-radius: $decor-radius-200;
|
border-radius: $decor-radius-200;
|
||||||
|
|
|
@ -4,14 +4,17 @@
|
||||||
main {
|
main {
|
||||||
@extend %app-view;
|
@extend %app-view;
|
||||||
}
|
}
|
||||||
|
%app-view > div > header {
|
||||||
|
@extend %app-view-header;
|
||||||
|
}
|
||||||
%app-view > div > div {
|
%app-view > div > div {
|
||||||
@extend %app-content;
|
@extend %app-view-content;
|
||||||
}
|
}
|
||||||
%app-view header form {
|
%app-view header form {
|
||||||
@extend %filter-bar;
|
@extend %filter-bar;
|
||||||
}
|
}
|
||||||
@media #{$--lt-spacious-page-header} {
|
@media #{$--lt-spacious-page-header} {
|
||||||
%app-view header .actions {
|
%app-view-header .actions {
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +33,8 @@ main {
|
||||||
%app-view h1 em {
|
%app-view h1 em {
|
||||||
color: $gray-600;
|
color: $gray-600;
|
||||||
}
|
}
|
||||||
%app-view header .actions a,
|
%app-view-header .actions a,
|
||||||
%app-view header .actions button {
|
%app-view-header .actions button {
|
||||||
@extend %button-compact;
|
@extend %button-compact;
|
||||||
}
|
}
|
||||||
%app-content div > dl {
|
%app-content div > dl {
|
||||||
|
@ -39,10 +42,10 @@ main {
|
||||||
}
|
}
|
||||||
[role='tabpanel'] > p:only-child,
|
[role='tabpanel'] > p:only-child,
|
||||||
.template-error > div,
|
.template-error > div,
|
||||||
%app-content > p:only-child,
|
%app-view-content > p:only-child,
|
||||||
%app-view > div.disabled > div,
|
%app-view > div.disabled > div,
|
||||||
%app-view.empty > div {
|
%app-view.empty > div {
|
||||||
@extend %app-content-empty;
|
@extend %app-view-content-empty;
|
||||||
}
|
}
|
||||||
[role='tabpanel'] > *:first-child {
|
[role='tabpanel'] > *:first-child {
|
||||||
margin-top: 1.25em;
|
margin-top: 1.25em;
|
||||||
|
|
|
@ -1,67 +1,67 @@
|
||||||
/* layout */
|
/* layout */
|
||||||
%app-view header > div:last-of-type > div:first-child {
|
%app-view-header > div:last-of-type > div:first-child {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
%app-view {
|
%app-view {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
%app-view header .actions {
|
%app-view-header .actions {
|
||||||
float: right;
|
float: right;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin-top: 9px;
|
margin-top: 9px;
|
||||||
}
|
}
|
||||||
%app-view header dl {
|
%app-view-header dl {
|
||||||
float: left;
|
float: left;
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
margin-right: 50px;
|
margin-right: 50px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
%app-view header dt {
|
%app-view-header dt {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
/* units */
|
/* units */
|
||||||
%app-view {
|
%app-view {
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
}
|
}
|
||||||
%app-view header + div > *:first-child {
|
%app-view-header + div > *:first-child {
|
||||||
margin-top: 1.8em;
|
margin-top: 1.8em;
|
||||||
}
|
}
|
||||||
%app-view h2 {
|
%app-view h2 {
|
||||||
padding-bottom: 0.2em;
|
padding-bottom: 0.2em;
|
||||||
margin-bottom: 0.2em;
|
margin-bottom: 0.2em;
|
||||||
}
|
}
|
||||||
%app-view header .actions > *:not(:last-child) {
|
%app-view-header .actions > *:not(:last-child) {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// content
|
// content
|
||||||
%app-content div > dl > dt {
|
%app-view-content div > dl > dt {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
%app-content div > dl {
|
%app-view-content div > dl {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
%app-content-empty {
|
%app-view-content-empty {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
%app-content form:not(:last-child) {
|
%app-view-content form:not(:last-child) {
|
||||||
margin-bottom: 2.2em;
|
margin-bottom: 2.2em;
|
||||||
}
|
}
|
||||||
%app-content div > dl > dt {
|
%app-view-content div > dl > dt {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
}
|
}
|
||||||
%app-content div > dl > dd {
|
%app-view-content div > dl > dd {
|
||||||
padding-left: 140px;
|
padding-left: 140px;
|
||||||
}
|
}
|
||||||
%app-content div > dl > * {
|
%app-view-content div > dl > * {
|
||||||
min-height: 1em;
|
min-height: 1em;
|
||||||
margin-bottom: 0.4em;
|
margin-bottom: 0.4em;
|
||||||
}
|
}
|
||||||
// TODO: Think about an %app-form or similar
|
// TODO: Think about an %app-form or similar
|
||||||
%app-content fieldset:not(.freetext-filter) {
|
%app-view-content fieldset:not(.freetext-filter) {
|
||||||
padding-bottom: 0.3em;
|
padding-bottom: 0.3em;
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,10 @@
|
||||||
.healthcheck-output {
|
.healthcheck-output {
|
||||||
@extend %healthcheck-output;
|
@extend %healthcheck-output;
|
||||||
}
|
}
|
||||||
|
%healthcheck-output em::before {
|
||||||
|
width: 250px;
|
||||||
|
/* TODO: All tooltips previously used */
|
||||||
|
/* nowrap, they shouldn't */
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
/**/
|
||||||
|
|
|
@ -12,45 +12,54 @@
|
||||||
%healthcheck-output > div {
|
%healthcheck-output > div {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
%healthcheck-output header,
|
||||||
|
%healthcheck-output dl:last-of-type {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
%healthcheck-output header {
|
%healthcheck-output header {
|
||||||
margin-bottom: 0.9em;
|
margin-bottom: 0.9em;
|
||||||
}
|
}
|
||||||
%healthcheck-output dl:not(:last-of-type) {
|
%healthcheck-output > div {
|
||||||
float: left;
|
// 100% minus the width of the icon space (26)
|
||||||
width: 25%;
|
width: calc(100% - 26px);
|
||||||
margin-right: 2%;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
%healthcheck-output dl:nth-of-type(3) {
|
%healthcheck-output dl {
|
||||||
width: 46%;
|
min-width: 110px;
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
%healthcheck-output dl:not(:last-of-type) dd {
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
%healthcheck-output dt {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
%healthcheck-output dl:last-of-type {
|
|
||||||
clear: both;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
%healthcheck-output dl:last-of-type dt {
|
|
||||||
margin-bottom: 0.3em;
|
|
||||||
}
|
|
||||||
%healthcheck-output dl:last-of-type dd {
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
%healthcheck-output dl > * {
|
%healthcheck-output dl > * {
|
||||||
float: none;
|
|
||||||
display: block;
|
display: block;
|
||||||
width: auto;
|
width: auto;
|
||||||
position: static;
|
position: static;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
%healthcheck-output dt {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
%healthcheck-output dd {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
%healthcheck-output dl:nth-last-of-type(2) {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
%healthcheck-output dl:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
%healthcheck-output dl:last-of-type dt {
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
%healthcheck-output pre {
|
%healthcheck-output pre {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
%healthcheck-output pre code {
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
%healthcheck-output .with-feedback {
|
%healthcheck-output .with-feedback {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -67,9 +76,10 @@
|
||||||
%healthcheck-output::before {
|
%healthcheck-output::before {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
%healthcheck-output dl:nth-last-of-type(2) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
%healthcheck-output dl:not(:last-of-type) {
|
%healthcheck-output dl:not(:last-of-type) {
|
||||||
float: none;
|
|
||||||
width: auto;
|
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,14 @@
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
%healthcheck-output dd em {
|
||||||
|
@extend %pill;
|
||||||
|
/*TODO: Should this be merged into %pill? */
|
||||||
|
cursor: default;
|
||||||
|
font-style: normal;
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
%healthcheck-output.passing::before {
|
%healthcheck-output.passing::before {
|
||||||
@extend %with-check-circle-fill-color-icon;
|
@extend %with-check-circle-fill-color-icon;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ th span em {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
white-space: normal !important;
|
white-space: normal !important;
|
||||||
}
|
}
|
||||||
|
/**/
|
||||||
|
|
||||||
/* ideally these would be in route css files, but left here as they */
|
/* ideally these would be in route css files, but left here as they */
|
||||||
/* accomplish the same thing (hide non-essential columns for tables) */
|
/* accomplish the same thing (hide non-essential columns for tables) */
|
||||||
|
|
|
@ -95,6 +95,9 @@ html.template-token.template-list main table tr th {
|
||||||
html.template-node.template-show main table.sessions tr {
|
html.template-node.template-show main table.sessions tr {
|
||||||
@extend %node-sessions-row;
|
@extend %node-sessions-row;
|
||||||
}
|
}
|
||||||
|
html.template-instance.template-show main table.exposedpaths tr {
|
||||||
|
@extend %instance-paths-row;
|
||||||
|
}
|
||||||
// this will get auto calculated later in tabular-collection.js
|
// this will get auto calculated later in tabular-collection.js
|
||||||
// keeping this here for reference
|
// keeping this here for reference
|
||||||
// %services-row > * {
|
// %services-row > * {
|
||||||
|
@ -110,6 +113,22 @@ html.template-node.template-show main table.sessions tr {
|
||||||
%instances-row > * {
|
%instances-row > * {
|
||||||
width: calc(100% / 5);
|
width: calc(100% / 5);
|
||||||
}
|
}
|
||||||
|
// instance-paths are for exposed paths
|
||||||
|
// we make the columns that need as much space as possible
|
||||||
|
// as wide as possible so 50% each minus enough room
|
||||||
|
// for the 3 port columns - we probably need a max of 55px
|
||||||
|
// for each port column so 55 * 3 = 165
|
||||||
|
// so column 1 and 5 are 50% - 165 each
|
||||||
|
// the 3 remaining columns split the 165 thats left between them
|
||||||
|
%instance-paths-row > *:nth-child(1),
|
||||||
|
%instance-paths-row > *:nth-child(5) {
|
||||||
|
width: calc(50% - 165px) !important;
|
||||||
|
}
|
||||||
|
%instance-paths-row > *:nth-child(2),
|
||||||
|
%instance-paths-row > *:nth-child(3),
|
||||||
|
%instance-paths-row > *:nth-child(4) {
|
||||||
|
width: 110px !important;
|
||||||
|
}
|
||||||
%tokens-row > *:first-child,
|
%tokens-row > *:first-child,
|
||||||
%tokens-minimal-row > *:not(last-child),
|
%tokens-minimal-row > *:not(last-child),
|
||||||
%tokens-row > *:nth-child(2),
|
%tokens-row > *:nth-child(2),
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
/* TODO: need to standardize on the selectors used here */
|
||||||
|
/* I would guess at the time of writing this we shojuld prefer */
|
||||||
|
/* classes */
|
||||||
html.template-instance.template-show #addresses table tr,
|
html.template-instance.template-show #addresses table tr,
|
||||||
html.template-instance.template-show #upstreams table tr,
|
html.template-instance.template-show #upstreams table tr,
|
||||||
html.template-instance.template-show #meta-data table tr {
|
html.template-instance.template-show #meta-data table tr,
|
||||||
|
html.template-instance.template-show table.exposedpaths tr {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<ul data-test-healthchecks>
|
<ul data-test-healthchecks>
|
||||||
{{#each (sort-by (action 'sortChecksByImportance') items) as |item| }}
|
{{#each (sort-by (action 'sortChecksByImportance') items) as |item| }}
|
||||||
|
{{! TODO: this component and its child should be moved to a single component }}
|
||||||
{{#healthcheck-output
|
{{#healthcheck-output
|
||||||
data-test-node-healthcheck=item.Name
|
data-test-node-healthcheck=item.Name
|
||||||
class=item.Status
|
class=item.Status
|
||||||
tagName='li'
|
tagName='li'
|
||||||
output=item.Output
|
|
||||||
}}
|
}}
|
||||||
{{#block-slot 'header'}}
|
{{#block-slot 'header'}}
|
||||||
<h3>{{item.Name}}</h3>
|
<h3>{{item.Name}}</h3>
|
||||||
|
@ -18,10 +18,42 @@
|
||||||
<dt>CheckID</dt>
|
<dt>CheckID</dt>
|
||||||
<dd>{{or item.CheckID '-'}}</dd>
|
<dd>{{or item.CheckID '-'}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
<dl>
|
||||||
|
<dt>Type</dt>
|
||||||
|
<dd>
|
||||||
|
{{item.Type}}
|
||||||
|
{{#if (and exposed (contains item.Type (array 'http' 'grpc')))}}
|
||||||
|
<em data-test-exposed="true" data-tooltip="Expose.checks is set to true, so all registered HTTP and gRPC check paths are exposed through Envoy for the Consul agent.">Exposed</em>
|
||||||
|
{{/if}}
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Notes</dt>
|
<dt>Notes</dt>
|
||||||
<dd>{{or item.Notes '-'}}</dd>
|
<dd>{{or item.Notes '-'}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
<dl>
|
||||||
|
{{#if (not-eq item.Type 'ttl')}}
|
||||||
|
<dt>Output</dt>
|
||||||
|
<dd>
|
||||||
|
<pre><code>{{item.Output}}</code></pre>
|
||||||
|
{{#feedback-dialog type='inline'}}
|
||||||
|
{{#block-slot 'action' as |success error|}}
|
||||||
|
{{copy-button success=(action success) error=(action error) clipboardText=item.Output title='copy output to clipboard'}}
|
||||||
|
{{/block-slot}}
|
||||||
|
{{#block-slot 'success' as |transition|}}
|
||||||
|
<p class={{transition}}>
|
||||||
|
Copied output!
|
||||||
|
</p>
|
||||||
|
{{/block-slot}}
|
||||||
|
{{#block-slot 'error' as |transition|}}
|
||||||
|
<p class={{transition}}>
|
||||||
|
Sorry, something went wrong!
|
||||||
|
</p>
|
||||||
|
{{/block-slot}}
|
||||||
|
{{/feedback-dialog}}
|
||||||
|
</dd>
|
||||||
|
{{/if}}
|
||||||
|
</dl>
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{/healthcheck-output}}
|
{{/healthcheck-output}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
@ -1,28 +1,8 @@
|
||||||
|
{{! TODO: this component and its parent should be moved to a single component }}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
<div>
|
<div>
|
||||||
<header>
|
<header>
|
||||||
{{#yield-slot 'header'}}{{yield}}{{/yield-slot}}
|
{{#yield-slot 'header'}}{{yield}}{{/yield-slot}}
|
||||||
</header>
|
</header>
|
||||||
{{#yield-slot 'content'}}{{yield}}{{/yield-slot}}
|
{{#yield-slot 'content'}}{{yield}}{{/yield-slot}}
|
||||||
<dl>
|
|
||||||
<dt>Output</dt>
|
|
||||||
<dd>
|
|
||||||
<pre><code>{{output}}</code></pre>
|
|
||||||
{{#feedback-dialog type='inline'}}
|
|
||||||
{{#block-slot 'action' as |success error|}}
|
|
||||||
{{copy-button success=(action success) error=(action error) clipboardText=output title='copy output to clipboard'}}
|
|
||||||
{{/block-slot}}
|
|
||||||
{{#block-slot 'success' as |transition|}}
|
|
||||||
<p class={{transition}}>
|
|
||||||
Copied output!
|
|
||||||
</p>
|
|
||||||
{{/block-slot}}
|
|
||||||
{{#block-slot 'error' as |transition|}}
|
|
||||||
<p class={{transition}}>
|
|
||||||
Sorry, something went wrong!
|
|
||||||
</p>
|
|
||||||
{{/block-slot}}
|
|
||||||
{{/feedback-dialog}}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<p>
|
||||||
|
You can expose individual HTTP paths like /metrics through Envoy for external services like Prometheus.
|
||||||
|
</p>
|
||||||
|
{{#tabular-collection
|
||||||
|
data-test-exposedpaths
|
||||||
|
class="exposedpaths"
|
||||||
|
items=item.Proxy.Expose.Paths as |path index|
|
||||||
|
}}
|
||||||
|
{{#block-slot 'header'}}
|
||||||
|
<th>Path</th>
|
||||||
|
<th>Protocol</th>
|
||||||
|
<th>Listener port</th>
|
||||||
|
<th>Local path port</th>
|
||||||
|
<th>Combined address<span><em role="tooltip">Service address, listener port, and path all combined into one URL.</em></span></th>
|
||||||
|
{{/block-slot}}
|
||||||
|
{{#block-slot 'row'}}
|
||||||
|
<td>
|
||||||
|
<span>{{path.Path}}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{path.Protocol}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{path.ListenerPort}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{path.LocalPathPort}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span data-test-combined-address>{{item.Address}}:{{path.ListenerPort}}{{path.Path}}</span>
|
||||||
|
</td>
|
||||||
|
{{/block-slot}}
|
||||||
|
{{/tabular-collection}}
|
|
@ -1,5 +1,5 @@
|
||||||
{{#if (gt item.ServiceChecks.length 0) }}
|
{{#if (gt item.ServiceChecks.length 0) }}
|
||||||
{{healthcheck-list items=item.ServiceChecks}}
|
{{healthcheck-list items=item.ServiceChecks exposed=proxy.ServiceProxy.Expose.Checks}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<p>
|
<p>
|
||||||
This instance has no service health checks.
|
This instance has no service health checks.
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'row'}}
|
{{#block-slot 'row'}}
|
||||||
<td>
|
<td>
|
||||||
<a data-test-destination-name>{{item.DestinationName}}</a>
|
<span data-test-destination-name>{{item.DestinationName}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td data-test-destination-datacenter>
|
<td data-test-destination-datacenter>
|
||||||
{{item.Datacenter}}
|
{{item.Datacenter}}
|
||||||
|
|
|
@ -72,6 +72,10 @@
|
||||||
(eq item.Kind 'connect-proxy')
|
(eq item.Kind 'connect-proxy')
|
||||||
'Upstreams' ''
|
'Upstreams' ''
|
||||||
)
|
)
|
||||||
|
(if
|
||||||
|
(and (eq item.Kind 'connect-proxy') (gt item.Proxy.Expose.Paths.length 0))
|
||||||
|
'Exposed Paths' ''
|
||||||
|
)
|
||||||
(if
|
(if
|
||||||
(eq item.Kind 'mesh-gateway')
|
(eq item.Kind 'mesh-gateway')
|
||||||
'Addresses' ''
|
'Addresses' ''
|
||||||
|
@ -91,6 +95,10 @@
|
||||||
(eq item.Kind 'connect-proxy')
|
(eq item.Kind 'connect-proxy')
|
||||||
(hash id=(slugify 'Upstreams') partial='dc/services/upstreams') ''
|
(hash id=(slugify 'Upstreams') partial='dc/services/upstreams') ''
|
||||||
)
|
)
|
||||||
|
(if
|
||||||
|
(and (eq item.Kind 'connect-proxy') (gt item.Proxy.Expose.Paths.length 0))
|
||||||
|
(hash id=(slugify 'Exposed Paths') partial='dc/services/exposedpaths') ''
|
||||||
|
)
|
||||||
(if
|
(if
|
||||||
(eq item.Kind 'mesh-gateway')
|
(eq item.Kind 'mesh-gateway')
|
||||||
(hash id=(slugify 'Addresses') partial='dc/services/addresses') ''
|
(hash id=(slugify 'Addresses') partial='dc/services/addresses') ''
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
||||||
"@ember/jquery": "^0.6.0",
|
"@ember/jquery": "^0.6.0",
|
||||||
"@ember/optional-features": "^0.7.0",
|
"@ember/optional-features": "^0.7.0",
|
||||||
"@hashicorp/consul-api-double": "^2.0.1",
|
"@hashicorp/consul-api-double": "^2.6.2",
|
||||||
"@hashicorp/ember-cli-api-double": "^2.0.0",
|
"@hashicorp/ember-cli-api-double": "^2.0.0",
|
||||||
"base64-js": "^1.3.0",
|
"base64-js": "^1.3.0",
|
||||||
"broccoli-asset-rev": "^3.0.0",
|
"broccoli-asset-rev": "^3.0.0",
|
||||||
|
|
|
@ -3,6 +3,18 @@ Feature: components / copy-button
|
||||||
Background:
|
Background:
|
||||||
Given 1 datacenter model with the value "dc-1"
|
Given 1 datacenter model with the value "dc-1"
|
||||||
Scenario: Clicking the copy button
|
Scenario: Clicking the copy button
|
||||||
|
Given 1 node model from yaml
|
||||||
|
---
|
||||||
|
ID: node-0
|
||||||
|
Checks:
|
||||||
|
- Name: gprc-check
|
||||||
|
Node: node-0
|
||||||
|
CheckID: grpc-check
|
||||||
|
Status: passing
|
||||||
|
Type: grpc
|
||||||
|
Output: The output
|
||||||
|
Notes: The notes
|
||||||
|
---
|
||||||
When I visit the node page for yaml
|
When I visit the node page for yaml
|
||||||
---
|
---
|
||||||
dc: dc-1
|
dc: dc-1
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: dc / services / instances / proxy: Show Proxy Service Instance
|
Feature: dc / services / instances / proxy: Show Proxy Service Instance
|
||||||
Scenario: A Proxy Service instance
|
Scenario: A Proxy Service instance with no exposed checks
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
And 1 instance model from yaml
|
And 1 instance model from yaml
|
||||||
---
|
---
|
||||||
|
@ -10,6 +10,9 @@ Feature: dc / services / instances / proxy: Show Proxy Service Instance
|
||||||
ID: service-0-proxy-with-id
|
ID: service-0-proxy-with-id
|
||||||
Proxy:
|
Proxy:
|
||||||
DestinationServiceName: service-0
|
DestinationServiceName: service-0
|
||||||
|
Expose:
|
||||||
|
Checks: false
|
||||||
|
Paths: []
|
||||||
Upstreams:
|
Upstreams:
|
||||||
- DestinationType: service
|
- DestinationType: service
|
||||||
DestinationName: service-1
|
DestinationName: service-1
|
||||||
|
@ -32,6 +35,9 @@ Feature: dc / services / instances / proxy: Show Proxy Service Instance
|
||||||
|
|
||||||
And I see serviceChecksIsSelected on the tabs
|
And I see serviceChecksIsSelected on the tabs
|
||||||
|
|
||||||
|
When I click serviceChecks on the tabs
|
||||||
|
And I don't see exposed on the serviceChecks
|
||||||
|
|
||||||
When I click upstreams on the tabs
|
When I click upstreams on the tabs
|
||||||
And I see upstreamsIsSelected on the tabs
|
And I see upstreamsIsSelected on the tabs
|
||||||
And I see 2 of the upstreams object
|
And I see 2 of the upstreams object
|
||||||
|
@ -45,5 +51,83 @@ Feature: dc / services / instances / proxy: Show Proxy Service Instance
|
||||||
- service
|
- service
|
||||||
- prepared_query
|
- prepared_query
|
||||||
---
|
---
|
||||||
|
And I don't see exposedPaths on the tabs
|
||||||
|
|
||||||
|
Scenario: A Proxy Service instance with no automatically exposed checks but with paths
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 instance model from yaml
|
||||||
|
---
|
||||||
|
- Service:
|
||||||
|
Kind: connect-proxy
|
||||||
|
Name: service-0-proxy
|
||||||
|
ID: service-0-proxy-with-id
|
||||||
|
Address: 10.0.0.1
|
||||||
|
Proxy:
|
||||||
|
DestinationServiceName: service-0
|
||||||
|
Expose:
|
||||||
|
Paths:
|
||||||
|
- Path: /grpc-metrics
|
||||||
|
Protocol: grpc
|
||||||
|
LocalPathPort: 8081
|
||||||
|
ListenerPort: 8080
|
||||||
|
- Path: /http-metrics
|
||||||
|
Protocol: http
|
||||||
|
LocalPathPort: 8082
|
||||||
|
ListenerPort: 8083
|
||||||
|
---
|
||||||
|
When I visit the instance page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
service: service-0-proxy
|
||||||
|
node: node-0
|
||||||
|
id: service-0-proxy-with-id
|
||||||
|
---
|
||||||
|
Then the url should be /dc1/services/service-0-proxy/node-0/service-0-proxy-with-id
|
||||||
|
And I see serviceChecksIsSelected on the tabs
|
||||||
|
|
||||||
|
When I click serviceChecks on the tabs
|
||||||
|
And I don't see exposed on the serviceChecks
|
||||||
|
|
||||||
|
When I click exposedPaths on the tabs
|
||||||
|
And I see exposedPaths on the tabs
|
||||||
|
And I see 2 of the exposedPaths object
|
||||||
|
And I see combinedAddress on the exposedPaths like yaml
|
||||||
|
---
|
||||||
|
- 10.0.0.1:8080/grpc-metrics
|
||||||
|
- 10.0.0.1:8083/http-metrics
|
||||||
|
---
|
||||||
|
Scenario: A Proxy Service instance with only automatically exposed checks but no paths
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 instance model from yaml
|
||||||
|
---
|
||||||
|
- Service:
|
||||||
|
Kind: connect-proxy
|
||||||
|
Name: service-0-proxy
|
||||||
|
ID: service-0-proxy-with-id
|
||||||
|
Address: 10.0.0.1
|
||||||
|
Proxy:
|
||||||
|
DestinationServiceName: service-0
|
||||||
|
Expose:
|
||||||
|
Checks: true
|
||||||
|
Paths: []
|
||||||
|
Checks:
|
||||||
|
- Name: http-check
|
||||||
|
Type: http
|
||||||
|
---
|
||||||
|
When I visit the instance page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
service: service-0-proxy
|
||||||
|
node: node-0
|
||||||
|
id: service-0-proxy-with-id
|
||||||
|
---
|
||||||
|
Then the url should be /dc1/services/service-0-proxy/node-0/service-0-proxy-with-id
|
||||||
|
And I see serviceChecksIsSelected on the tabs
|
||||||
|
|
||||||
|
And I don't see exposedPaths on the tabs
|
||||||
|
|
||||||
|
When I click serviceChecks on the tabs
|
||||||
|
And I don't see exposed on the serviceChecks
|
||||||
|
|
||||||
|
When I click nodeChecks on the tabs
|
||||||
|
And I don't see exposed on the nodeChecks
|
||||||
|
|
|
@ -29,6 +29,7 @@ Feature: dc / services / instances / show: Show Service Instance
|
||||||
Output: Output of check
|
Output: Output of check
|
||||||
Status: warning
|
Status: warning
|
||||||
- Name: Service check
|
- Name: Service check
|
||||||
|
Type: http
|
||||||
ServiceID: service-0
|
ServiceID: service-0
|
||||||
Output: Output of check
|
Output: Output of check
|
||||||
Status: critical
|
Status: critical
|
||||||
|
@ -45,13 +46,13 @@ Feature: dc / services / instances / show: Show Service Instance
|
||||||
Output: Output of check
|
Output: Output of check
|
||||||
Status: critical
|
Status: critical
|
||||||
---
|
---
|
||||||
And 1 proxy model from yaml
|
Scenario: A Service instance has no Proxy
|
||||||
|
Given 1 proxy model from yaml
|
||||||
---
|
---
|
||||||
- ServiceProxy:
|
- ServiceProxy:
|
||||||
DestinationServiceName: service-1
|
DestinationServiceName: service-1
|
||||||
DestinationServiceID: ~
|
DestinationServiceID: ~
|
||||||
---
|
---
|
||||||
Scenario: A Service instance has no Proxy
|
|
||||||
When I visit the instance page for yaml
|
When I visit the instance page for yaml
|
||||||
---
|
---
|
||||||
dc: dc1
|
dc: dc1
|
||||||
|
@ -99,6 +100,63 @@ Feature: dc / services / instances / show: Show Service Instance
|
||||||
Then the url should be /dc1/services/service-0/node-0/service-0-with-id
|
Then the url should be /dc1/services/service-0/node-0/service-0-with-id
|
||||||
And an external edit results in 0 instance models
|
And an external edit results in 0 instance models
|
||||||
And pause until I see the text "deregistered" in "[data-notification]"
|
And pause until I see the text "deregistered" in "[data-notification]"
|
||||||
|
|
||||||
|
Scenario: A Service instance with a Proxy with only automatically exposed checks but no paths
|
||||||
|
Given 1 proxy model from yaml
|
||||||
|
---
|
||||||
|
- ServiceProxy:
|
||||||
|
DestinationServiceName: service-0
|
||||||
|
DestinationServiceID: ~
|
||||||
|
Expose:
|
||||||
|
Checks: true
|
||||||
|
Paths: []
|
||||||
|
---
|
||||||
|
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/another-node/service-0-with-id
|
||||||
|
And I see serviceChecksIsSelected on the tabs
|
||||||
|
|
||||||
|
And I don't see exposedPaths on the tabs
|
||||||
|
|
||||||
|
When I click serviceChecks on the tabs
|
||||||
|
And I see exposed on the serviceChecks
|
||||||
|
|
||||||
|
When I click nodeChecks on the tabs
|
||||||
|
And I don't see exposed on the nodeChecks
|
||||||
|
|
||||||
|
Scenario: A Service Instance with a Proxy with no automatically exposed checks
|
||||||
|
Given 1 proxy model from yaml
|
||||||
|
---
|
||||||
|
- ServiceProxy:
|
||||||
|
DestinationServiceName: service-0
|
||||||
|
DestinationServiceID: ~
|
||||||
|
Expose:
|
||||||
|
Checks: false
|
||||||
|
Paths: []
|
||||||
|
---
|
||||||
|
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/another-node/service-0-with-id
|
||||||
|
And I see serviceChecksIsSelected on the tabs
|
||||||
|
|
||||||
|
And I don't see exposedPaths on the tabs
|
||||||
|
|
||||||
|
When I click serviceChecks on the tabs
|
||||||
|
And I don't see exposed on the serviceChecks
|
||||||
|
|
||||||
|
When I click nodeChecks on the tabs
|
||||||
|
And I don't see exposed on the nodeChecks
|
||||||
|
|
||||||
@ignore
|
@ignore
|
||||||
Scenario: A Service Instance's proxy blocking query is closed when the instance is deregistered
|
Scenario: A Service Instance's proxy blocking query is closed when the instance is deregistered
|
||||||
Then ok
|
Then ok
|
||||||
|
|
|
@ -12,23 +12,12 @@ module('Integration | Component | healthcheck output', function(hooks) {
|
||||||
|
|
||||||
await render(hbs`{{healthcheck-output}}`);
|
await render(hbs`{{healthcheck-output}}`);
|
||||||
|
|
||||||
assert.notEqual(
|
assert.equal(find('*').textContent.trim(), '');
|
||||||
find('*')
|
|
||||||
.textContent.trim()
|
|
||||||
.indexOf('Output'),
|
|
||||||
-1
|
|
||||||
);
|
|
||||||
|
|
||||||
// Template block usage:
|
// Template block usage:
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
{{#healthcheck-output}}{{/healthcheck-output}}
|
{{#healthcheck-output}}{{/healthcheck-output}}
|
||||||
`);
|
`);
|
||||||
|
assert.equal(find('*').textContent.trim(), '');
|
||||||
assert.notEqual(
|
|
||||||
find('*')
|
|
||||||
.textContent.trim()
|
|
||||||
.indexOf('Output'),
|
|
||||||
-1
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -73,7 +73,7 @@ test('findBySlug returns the correct data for item endpoint', function(assert) {
|
||||||
const nodes = payload.Nodes;
|
const nodes = payload.Nodes;
|
||||||
const service = payload.Nodes[0];
|
const service = payload.Nodes[0];
|
||||||
service.Nodes = nodes;
|
service.Nodes = nodes;
|
||||||
service.Tags = payload.Nodes[0].Service.Tags;
|
service.Tags = [...new Set(payload.Nodes[0].Service.Tags)];
|
||||||
service.meta = {
|
service.meta = {
|
||||||
cursor: undefined,
|
cursor: undefined,
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,17 +7,25 @@ export default function(visitable, attribute, collection, text, radiogroup) {
|
||||||
'node-checks',
|
'node-checks',
|
||||||
'addresses',
|
'addresses',
|
||||||
'upstreams',
|
'upstreams',
|
||||||
|
'exposed-paths',
|
||||||
'tags',
|
'tags',
|
||||||
'meta-data',
|
'meta-data',
|
||||||
]),
|
]),
|
||||||
serviceChecks: collection('#service-checks [data-test-healthchecks] li', {}),
|
serviceChecks: collection('#service-checks [data-test-healthchecks] li', {
|
||||||
nodeChecks: collection('#node-checks [data-test-healthchecks] li', {}),
|
exposed: attribute('data-test-exposed', '[data-test-exposed]'),
|
||||||
|
}),
|
||||||
|
nodeChecks: collection('#node-checks [data-test-healthchecks] li', {
|
||||||
|
exposed: attribute('data-test-exposed', '[data-test-exposed]'),
|
||||||
|
}),
|
||||||
upstreams: collection('#upstreams [data-test-tabular-row]', {
|
upstreams: collection('#upstreams [data-test-tabular-row]', {
|
||||||
name: text('[data-test-destination-name]'),
|
name: text('[data-test-destination-name]'),
|
||||||
datacenter: text('[data-test-destination-datacenter]'),
|
datacenter: text('[data-test-destination-datacenter]'),
|
||||||
type: text('[data-test-destination-type]'),
|
type: text('[data-test-destination-type]'),
|
||||||
address: text('[data-test-local-bind-address]'),
|
address: text('[data-test-local-bind-address]'),
|
||||||
}),
|
}),
|
||||||
|
exposedPaths: collection('#exposed-paths [data-test-tabular-row]', {
|
||||||
|
combinedAddress: text('[data-test-combined-address]'),
|
||||||
|
}),
|
||||||
addresses: collection('#addresses [data-test-tabular-row]', {
|
addresses: collection('#addresses [data-test-tabular-row]', {
|
||||||
address: text('[data-test-address]'),
|
address: text('[data-test-address]'),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -991,10 +991,10 @@
|
||||||
faker "^4.1.0"
|
faker "^4.1.0"
|
||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
|
|
||||||
"@hashicorp/consul-api-double@^2.0.1":
|
"@hashicorp/consul-api-double@^2.6.2":
|
||||||
version "2.5.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.5.0.tgz#d9540a38ee652d55ed90956850c9e5cbcde89454"
|
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.8.0.tgz#d4bd0999f2ce8419112934bb66af9c4c009c503e"
|
||||||
integrity sha512-RZcVIPQ4M4TZzFe2mWm7M5w28yOIpVgiYZI5ax+JG0Yr5TVbhJPMxhdb1es73cILuqIi9Fr+73OJ5IAospgPBw==
|
integrity sha512-Q25JJA8KDMnSwKLvCySokumaEIccq/uI0sRpHDuLjBzOA5gZUnBqrDlQUYlTPVYr98DEeiHCJiAacAnOpRpf7Q==
|
||||||
|
|
||||||
"@hashicorp/ember-cli-api-double@^2.0.0":
|
"@hashicorp/ember-cli-api-double@^2.0.0":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
|
@ -2173,6 +2173,10 @@ babel-plugin-transform-es2015-sticky-regex@^6.22.0:
|
||||||
version "6.24.1"
|
version "6.24.1"
|
||||||
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
|
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
|
||||||
integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw=
|
integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw=
|
||||||
|
dependencies:
|
||||||
|
babel-helper-regex "^6.24.1"
|
||||||
|
babel-runtime "^6.22.0"
|
||||||
|
babel-types "^6.24.1"
|
||||||
|
|
||||||
babel-plugin-transform-es2015-template-literals@^6.22.0:
|
babel-plugin-transform-es2015-template-literals@^6.22.0:
|
||||||
version "6.22.0"
|
version "6.22.0"
|
||||||
|
|
Loading…
Reference in New Issue