From 920f91f69ff2b8f558ff15985e8d9328c4092e5e Mon Sep 17 00:00:00 2001 From: Kenia <19161242+kaxcode@users.noreply.github.com> Date: Thu, 7 May 2020 09:57:15 -0400 Subject: [PATCH] ui: Redesign - Instance Detail Proxy Info tab (#7745) * Fix clickFirstAnchor bug * Create Proxy Info Tab for Instance Detail Page * Create tests for ProxyInfo and update other scenarios with Proxy data * ui: Refactors our app-view/%app-view component (#7752) Co-authored-by: John Cowen --- ui-v2/app/components/app-view/index.hbs | 20 +- .../components/copy-button-feedback/index.js | 4 +- .../app/components/healthcheck-list/index.hbs | 7 +- ui-v2/app/components/list-collection/index.js | 2 +- ui-v2/app/router.js | 3 + ui-v2/app/routes/dc/services/instance.js | 15 +- .../app/routes/dc/services/instance/proxy.js | 14 ++ ui-v2/app/styles/app.scss | 1 + .../styles/base/components/table/layout.scss | 2 +- .../styles/base/components/tabs/layout.scss | 7 +- ui-v2/app/styles/components/app-view.scss | 67 +----- .../app/styles/components/app-view/index.scss | 12 + .../styles/components/app-view/layout.scss | 69 +++--- .../app/styles/components/app-view/skin.scss | 63 ++--- .../components/composite-row/index.scss | 9 +- .../components/composite-row/layout.scss | 12 +- .../styles/components/composite-row/skin.scss | 9 + .../consul-service-instance-list.scss | 2 +- .../components/consul-service-list.scss | 2 +- .../components/dom-recycling-table/index.scss | 4 + .../styles/components/filter-bar/layout.scss | 5 +- .../components/healthcheck-output/layout.scss | 4 +- .../components/healthcheck-output/skin.scss | 4 +- ui-v2/app/styles/components/table.scss | 47 ++-- ui-v2/app/styles/core/typography.scss | 4 +- .../app/styles/routes/dc/services/index.scss | 17 ++ .../app/templates/dc/acls/policies/index.hbs | 8 +- ui-v2/app/templates/dc/acls/roles/index.hbs | 8 +- ui-v2/app/templates/dc/acls/tokens/index.hbs | 2 + ui-v2/app/templates/dc/nodes/show.hbs | 2 + ui-v2/app/templates/dc/services/instance.hbs | 45 +--- .../dc/services/instance/exposedpaths.hbs | 37 --- .../templates/dc/services/instance/proxy.hbs | 96 ++++++++ .../dc/services/instance/upstreams.hbs | 43 ---- ui-v2/app/templates/dc/services/show.hbs | 8 +- .../dc/services/instances/gateway.feature | 6 + .../dc/services/instances/proxyinfo.feature | 215 ++++++++++++++++++ .../dc/services/instances/show.feature | 36 ++- .../dc/services/instances/proxyinfo-steps.js | 10 + ui-v2/tests/pages/dc/services/instance.js | 27 +-- ui-v2/tests/pages/dc/services/show.js | 2 +- 41 files changed, 624 insertions(+), 326 deletions(-) create mode 100644 ui-v2/app/routes/dc/services/instance/proxy.js create mode 100644 ui-v2/app/styles/routes/dc/services/index.scss delete mode 100644 ui-v2/app/templates/dc/services/instance/exposedpaths.hbs create mode 100644 ui-v2/app/templates/dc/services/instance/proxy.hbs delete mode 100644 ui-v2/app/templates/dc/services/instance/upstreams.hbs create mode 100644 ui-v2/tests/acceptance/dc/services/instances/proxyinfo.feature create mode 100644 ui-v2/tests/acceptance/steps/dc/services/instances/proxyinfo-steps.js diff --git a/ui-v2/app/components/app-view/index.hbs b/ui-v2/app/components/app-view/index.hbs index c27e84034a76..b66f00fbcaeb 100644 --- a/ui-v2/app/components/app-view/index.hbs +++ b/ui-v2/app/components/app-view/index.hbs @@ -45,19 +45,25 @@ {{/each}}
-
- {{#if authorized}} - {{yield}} - {{/if}} -
-
{{#if authorized}} {{/if}} - {{yield}} +
+ + {{yield}} + +
+ {{#if authorized}} + {{yield}} + {{/if}} +
+
+ + {{yield}} +
{{#if authorized}} diff --git a/ui-v2/app/components/copy-button-feedback/index.js b/ui-v2/app/components/copy-button-feedback/index.js index 55706477346c..4798652642ba 100644 --- a/ui-v2/app/components/copy-button-feedback/index.js +++ b/ui-v2/app/components/copy-button-feedback/index.js @@ -1,3 +1,5 @@ import Component from '@ember/component'; -export default Component.extend({}); +export default Component.extend({ + tagName: '', +}); diff --git a/ui-v2/app/components/healthcheck-list/index.hbs b/ui-v2/app/components/healthcheck-list/index.hbs index 17abb62e84b0..47cb070c068f 100644 --- a/ui-v2/app/components/healthcheck-list/index.hbs +++ b/ui-v2/app/components/healthcheck-list/index.hbs @@ -7,8 +7,13 @@
+ {{#if (eq item.ServiceName "")}} +
NodeName
+
{{item.Node}}
+ {{else}}
ServiceName
-
{{or item.ServiceName '-'}}
+
{{item.ServiceName}}
+ {{/if}}
CheckID
diff --git a/ui-v2/app/components/list-collection/index.js b/ui-v2/app/components/list-collection/index.js index dc830db62c8d..23c3cc8394df 100644 --- a/ui-v2/app/components/list-collection/index.js +++ b/ui-v2/app/components/list-collection/index.js @@ -47,7 +47,7 @@ export default Component.extend(WithResizing, { }, actions: { click: function(e) { - return this.dom.clickFirstAnchor(e, 'li'); + return this.dom.clickFirstAnchor(e, '.list-collection > ul > li'); }, }, }); diff --git a/ui-v2/app/router.js b/ui-v2/app/router.js index da6514f63768..d11aa5a95219 100644 --- a/ui-v2/app/router.js +++ b/ui-v2/app/router.js @@ -31,6 +31,9 @@ export const routes = { healthchecks: { _options: { path: '/health-checks' }, }, + proxy: { + _options: { path: '/proxy' }, + }, upstreams: { _options: { path: '/upstreams' }, }, diff --git a/ui-v2/app/routes/dc/services/instance.js b/ui-v2/app/routes/dc/services/instance.js index 40db8a5e4351..cb27fbb2f5e7 100644 --- a/ui-v2/app/routes/dc/services/instance.js +++ b/ui-v2/app/routes/dc/services/instance.js @@ -16,12 +16,25 @@ export default Route.extend({ // its highly unlikely that a service will suddenly change to being a // connect-proxy or vice versa so leave as is for now return hash({ - proxy: + proxyMeta: // proxies and mesh-gateways can't have proxies themselves so don't even look ['connect-proxy', 'mesh-gateway'].includes(get(model.item, 'Kind')) ? null : this.proxyRepo.findInstanceBySlug(params.id, params.node, params.name, dc, nspace), ...model, + }).then(model => { + if (typeof get(model, 'proxyMeta.ServiceID') === 'undefined') { + return model; + } + const proxyName = get(model, 'proxyMeta.ServiceName'); + const proxyID = get(model, 'proxyMeta.ServiceID'); + const proxyNode = get(model, 'proxyMeta.Node'); + return hash({ + // Proxies have identical dc/nspace as their parent instance + // No need to use Proxy's dc/nspace response + proxy: this.repo.findInstanceBySlug(proxyID, proxyNode, proxyName, dc, nspace), + ...model, + }); }); }); }, diff --git a/ui-v2/app/routes/dc/services/instance/proxy.js b/ui-v2/app/routes/dc/services/instance/proxy.js new file mode 100644 index 000000000000..9793d62c02f1 --- /dev/null +++ b/ui-v2/app/routes/dc/services/instance/proxy.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default Route.extend({ + model: function() { + const parent = this.routeName + .split('.') + .slice(0, -1) + .join('.'); + return this.modelFor(parent); + }, + setupController: function(controller, model) { + controller.setProperties(model); + }, +}); diff --git a/ui-v2/app/styles/app.scss b/ui-v2/app/styles/app.scss index e9d733c4e39e..5b0a65497a5b 100644 --- a/ui-v2/app/styles/app.scss +++ b/ui-v2/app/styles/app.scss @@ -14,6 +14,7 @@ @import 'routes/dc/settings/index'; @import 'routes/dc/nodes/index'; +@import 'routes/dc/services/index'; @import 'routes/dc/intention/index'; @import 'routes/dc/kv/index'; @import 'routes/dc/acls/index'; diff --git a/ui-v2/app/styles/base/components/table/layout.scss b/ui-v2/app/styles/base/components/table/layout.scss index f7c52a485fed..52fa643a4fb0 100644 --- a/ui-v2/app/styles/base/components/table/layout.scss +++ b/ui-v2/app/styles/base/components/table/layout.scss @@ -29,7 +29,6 @@ %table td.no-actions ~ .actions { display: none; } -%table td:not(.actions), %table td:not(.actions) > *:only-child { overflow-x: hidden; } @@ -41,6 +40,7 @@ } %table td { height: 50px; + vertical-align: middle; } %table caption { margin-bottom: 0.8em; diff --git a/ui-v2/app/styles/base/components/tabs/layout.scss b/ui-v2/app/styles/base/components/tabs/layout.scss index 42862f9cab0e..92d6a85494f2 100644 --- a/ui-v2/app/styles/base/components/tabs/layout.scss +++ b/ui-v2/app/styles/base/components/tabs/layout.scss @@ -22,9 +22,12 @@ display: inline-block; padding: 16px 13px; } -%tab-section section h3 { - margin: 24px 0; +%tab-section section { + padding-bottom: 24px; } %tab-section section:not(:last-child) { border-bottom: 1px solid $gray-200; } +%tab-section section > h3 { + margin: 24px 0; +} diff --git a/ui-v2/app/styles/components/app-view.scss b/ui-v2/app/styles/components/app-view.scss index c05db39e75a2..5be681a7e1e7 100644 --- a/ui-v2/app/styles/components/app-view.scss +++ b/ui-v2/app/styles/components/app-view.scss @@ -4,66 +4,19 @@ @import '../base/components/popover-menu/index'; -%app-view-header .actions > [type='checkbox'] { - @extend %more-popover-menu; -} -%more-popover-menu-panel [type='checkbox']:checked ~ * { - /* this needs to autocalculate */ - min-height: 143px; - max-height: 143px; -} -%more-popover-menu-panel [id$='logout']:checked ~ * { - /* this needs to autocalculate */ - min-height: 163px; - max-height: 163px; -} -%more-popover-menu-panel [id$='delete']:checked ~ ul label[for$='delete'] + [role='menu'], -%more-popover-menu-panel [id$='logout']:checked ~ ul label[for$='logout'] + [role='menu'], -%more-popover-menu-panel [id$='use']:checked ~ ul label[for$='use'] + [role='menu'] { - display: block; -} -%app-view-header .actions label + div { - // We need this extra to allow tooltips to show +%app-view-actions label + div { + /* We need this extra to allow tooltips to show */ overflow: visible !important; } main { @extend %app-view; } -%app-view > div > header { - @extend %app-view-header; -} -%app-view > div > div { - @extend %app-view-content; -} -%app-view header form { +%app-view-header form { @extend %filter-bar; } -@media #{$--lt-spacious-page-header} { - %app-view-header .actions { - margin-top: 9px; - } -} -// TODO: This should be its own component -%app-view h1 { - padding-bottom: 0.2em; -} -%app-view h1 span[data-tooltip] { - @extend %with-external-source-icon; - margin-top: 13px; -} -%app-view h1 span.kind-proxy { - @extend %frame-gray-900; - @extend %pill; -} -%app-view h1 span.kind-proxy::before { - width: 0.3em !important; -} -%app-view h1 em { - color: $gray-600; -} -%app-view-header .actions a, -%app-view-header .actions button { +%app-view-actions a, +%app-view-actions button { @extend %button-compact; } %app-view-content div > dl { @@ -97,12 +50,14 @@ main { display: none; } } +@media #{$--lt-spacious-page-header} { + %app-view-actions { + margin-top: 9px; + } +} // reduced search magnifying icon layout @media #{$--lt-horizontal-selects} { - %app-view header h1 { - display: inline-block; - } - %app-view header h1 { + %app-view-header h1 { display: inline-block; } // on the instance detail page we don't have the magnifier diff --git a/ui-v2/app/styles/components/app-view/index.scss b/ui-v2/app/styles/components/app-view/index.scss index bc182521964a..008b0a8ae734 100644 --- a/ui-v2/app/styles/components/app-view/index.scss +++ b/ui-v2/app/styles/components/app-view/index.scss @@ -1,2 +1,14 @@ @import './skin'; @import './layout'; +%app-view > div > header { + @extend %app-view-header; +} +%app-view-header .title { + @extend %app-view-title; +} +%app-view-header .actions { + @extend %app-view-actions; +} +%app-view > div > div { + @extend %app-view-content; +} diff --git a/ui-v2/app/styles/components/app-view/layout.scss b/ui-v2/app/styles/components/app-view/layout.scss index d2ee8cf442e8..30f47d165b9c 100644 --- a/ui-v2/app/styles/components/app-view/layout.scss +++ b/ui-v2/app/styles/components/app-view/layout.scss @@ -1,67 +1,61 @@ /* layout */ -%app-view-header > div:last-of-type > div:first-child { - flex-grow: 1; -} %app-view { position: relative; } -%app-view-header .actions { - float: right; +%app-view-title { + display: flex; + align-items: center; +} +%app-view-actions { + margin-left: auto; display: flex; align-items: flex-start; - margin-top: 9px; } %app-view-header dl { float: left; - margin-top: 25px; - margin-right: 50px; - margin-bottom: 20px; -} -%app-view-header dt { - font-weight: bold; -} -%app-view-header dd > a { - color: $black; -} -%app-view-header .title-bar { - display: flex; - align-items: center; -} -%app-view-header .title-bar > h1 { - border: 0; -} -%app-view-header .title-bar > span { - margin-left: 8px; } /* units */ %app-view { margin-top: 50px; } +/* give anything after the header a bit of room */ %app-view-header + div > *:first-child { margin-top: 1.8em; } -%app-view h2 { +%app-view-title { padding-bottom: 0.2em; - margin-bottom: 0.5em; } -%app-view-header .actions > *:not(:last-child) { +%app-view-title > :not(:last-child) { + margin-right: 8px; +} +%app-view-actions { + margin-top: 9px; +} +%app-view-actions > *:not(:last-child) { margin-right: 12px; } - -// content -%app-view-content div > dl > dt { - position: absolute; +%app-view-header dl { + margin-top: 19px; + margin-bottom: 23px; + margin-right: 50px; } -%app-view-content div > dl { - position: relative; + +/* content */ +%app-view-content h2 { + padding-bottom: 0.2em; + margin-bottom: 0.5em; } %app-view-content-empty { margin-top: 0; padding: 50px; text-align: center; } -%app-view-content form:not(:last-child) { - margin-bottom: 2.2em; +/* this should probably be its own component */ +%app-view-content div > dl { + position: relative; +} +%app-view-content div > dl > dt { + position: absolute; } %app-view-content div > dl > dt { width: 140px; @@ -73,7 +67,8 @@ min-height: 1em; margin-bottom: 0.4em; } -// TODO: Think about an %app-form or similar +/* */ +/* TODO: Think about an %app-form or similar */ %app-view-content fieldset:not(.freetext-filter) { padding-bottom: 0.3em; margin-bottom: 2em; diff --git a/ui-v2/app/styles/components/app-view/skin.scss b/ui-v2/app/styles/components/app-view/skin.scss index 7d10f4f65578..65b6ac2dfcec 100644 --- a/ui-v2/app/styles/components/app-view/skin.scss +++ b/ui-v2/app/styles/components/app-view/skin.scss @@ -1,52 +1,33 @@ -/*TODO: Rename this to %app-view-brand-icon or similar */ -%with-external-source-icon { - background-repeat: no-repeat; - background-size: contain; - width: 18px; - height: 18px; - - --kubernetes-icon: #{$kubernetes-logo-color-svg}; - --terraform-icon: #{$terraform-logo-color-svg}; - --nomad-icon: #{$nomad-logo-color-svg}; - --consul-icon: #{$consul-logo-color-svg}; - --aws-icon: #{$aws-logo-color-svg}; -} -%app-view h2, -%app-view fieldset { +%app-view-content-empty { + @extend %frame-gray-500; +} +%app-view-title { + border-bottom: $decor-border-200; +} +%app-view-content h2, +%app-view-content fieldset { border-bottom: $decor-border-200; } -%app-view fieldset h2 { +%app-view-content fieldset h2 { border-bottom: none; } -@media #{$--horizontal-selects} { - %app-view header h1 { - border-bottom: $decor-border-200; - } +%app-view-header h1 > em { + color: $gray-600; +} +%app-view-header dd > a { + color: $black; } -@media #{$--lt-horizontal-selects} { - %app-view header > div > div:last-child { - border-bottom: $decor-border-200; - } +%app-view-content div > dl > dd { + color: $gray-400; } -%app-view header > div > div:last-child, -%app-view header h1, -%app-view h2, -%app-view fieldset { +%app-view-title, +%app-view-content h2, +%app-view-content fieldset { border-color: $gray-200; } // We know that any sibling navs might have a top border -// by default. As its squashed up to a h1, in this +// by default. As its squashed up to a %app-view-title, in this // case hide its border to avoid double border -@media #{$--horizontal-selects} { - %app-view header h1 ~ nav { - border-top: 0 !important; - } -} -%app-view-content div > dl > dd { - color: $gray-400; -} -[role='tabpanel'] > p:only-child, -.template-error > div, -%app-view-content > p:only-child { - @extend %frame-gray-500; +%app-view-title ~ nav { + border-top: 0 !important; } diff --git a/ui-v2/app/styles/components/composite-row/index.scss b/ui-v2/app/styles/components/composite-row/index.scss index f6aadc70c3c2..4b20237c606c 100644 --- a/ui-v2/app/styles/components/composite-row/index.scss +++ b/ui-v2/app/styles/components/composite-row/index.scss @@ -1,11 +1,12 @@ @import './layout'; @import './skin'; -%composite-row:hover, -%composite-row:focus, -%composite-row:active { +%with-composite-row-intent:hover, +%with-composite-row-intent:focus, +%with-composite-row-intent:active { @extend %composite-row-intent; } -%composite-row > a { +%composite-row > a, +%composite-row > p { @extend %composite-row-header; } %composite-row > ul { diff --git a/ui-v2/app/styles/components/composite-row/layout.scss b/ui-v2/app/styles/components/composite-row/layout.scss index be8b009e70a1..7175b216680d 100644 --- a/ui-v2/app/styles/components/composite-row/layout.scss +++ b/ui-v2/app/styles/components/composite-row/layout.scss @@ -1,11 +1,13 @@ %composite-row { - cursor: pointer; display: block; box-sizing: border-box; padding: 12px; padding-right: 0; border: 1px solid; } +%composite-row-header { + margin-bottom: 0 !important; +} %composite-row-intent { border: 1px solid; position: relative; @@ -23,3 +25,11 @@ %composite-row-detail .node::before { margin-top: 2px; } +// In this case we do not need a background on the icon +%composite-row-detail .port button { + padding: 0 !important; + margin-top: 1px !important; +} +%composite-row-detail .port button:hover { + background-color: transparent !important; +} diff --git a/ui-v2/app/styles/components/composite-row/skin.scss b/ui-v2/app/styles/components/composite-row/skin.scss index 9ab9fc7e39eb..b80065922229 100644 --- a/ui-v2/app/styles/components/composite-row/skin.scss +++ b/ui-v2/app/styles/components/composite-row/skin.scss @@ -8,6 +8,7 @@ %composite-row-intent { border-color: $gray-200; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + cursor: pointer; } %composite-row-header { color: $black; @@ -53,3 +54,11 @@ @extend %with-swap-horizontal-mask, %as-pseudo; background-color: $gray-500; } +%composite-row .datacenter::before { + @extend %with-user-organization-mask, %as-pseudo; + background-color: $gray-500; +} +%composite-row .nspace::before { + @extend %with-folder-outline-mask, %as-pseudo; + background-color: $gray-500; +} diff --git a/ui-v2/app/styles/components/consul-service-instance-list.scss b/ui-v2/app/styles/components/consul-service-instance-list.scss index 8ae21f9bad0f..55b68c798a7e 100644 --- a/ui-v2/app/styles/components/consul-service-instance-list.scss +++ b/ui-v2/app/styles/components/consul-service-instance-list.scss @@ -5,5 +5,5 @@ @extend %consul-service-instance-row; } %consul-service-instance-row { - @extend %composite-row; + @extend %composite-row, %with-composite-row-intent; } diff --git a/ui-v2/app/styles/components/consul-service-list.scss b/ui-v2/app/styles/components/consul-service-list.scss index eb99bb36d99c..d6d41bbbf598 100644 --- a/ui-v2/app/styles/components/consul-service-list.scss +++ b/ui-v2/app/styles/components/consul-service-list.scss @@ -5,7 +5,7 @@ @extend %consul-service-row; } %consul-service-row { - @extend %composite-row; + @extend %composite-row, %with-composite-row-intent; } %consul-service-row > ul { margin-left: 26px; diff --git a/ui-v2/app/styles/components/dom-recycling-table/index.scss b/ui-v2/app/styles/components/dom-recycling-table/index.scss index 0820684ccbcf..8e800a26bd4b 100644 --- a/ui-v2/app/styles/components/dom-recycling-table/index.scss +++ b/ui-v2/app/styles/components/dom-recycling-table/index.scss @@ -1 +1,5 @@ @import './layout'; + +%dom-recycling-table { + @extend %table-flex; +} diff --git a/ui-v2/app/styles/components/filter-bar/layout.scss b/ui-v2/app/styles/components/filter-bar/layout.scss index cdd857fe1b99..385694c23119 100644 --- a/ui-v2/app/styles/components/filter-bar/layout.scss +++ b/ui-v2/app/styles/components/filter-bar/layout.scss @@ -1,8 +1,11 @@ %filter-bar { padding: 4px; display: block; + margin-bottom: 8px; margin-top: 0 !important; - margin-bottom: 8px !important; +} +%filter-bar + :not(.notice) { + margin-top: 1.8em; } @media #{$--horizontal-filters} { %filter-bar { diff --git a/ui-v2/app/styles/components/healthcheck-output/layout.scss b/ui-v2/app/styles/components/healthcheck-output/layout.scss index 8917913eaa2e..d6232b316240 100644 --- a/ui-v2/app/styles/components/healthcheck-output/layout.scss +++ b/ui-v2/app/styles/components/healthcheck-output/layout.scss @@ -1,9 +1,9 @@ %healthcheck-output { display: flex; - padding: 20px 16px; padding-right: 24px; - +} +%healthcheck-output:not(:last-child) { margin-bottom: 24px; } %healthcheck-output::before { diff --git a/ui-v2/app/styles/components/healthcheck-output/skin.scss b/ui-v2/app/styles/components/healthcheck-output/skin.scss index 33b20f513582..66faff029097 100644 --- a/ui-v2/app/styles/components/healthcheck-output/skin.scss +++ b/ui-v2/app/styles/components/healthcheck-output/skin.scss @@ -3,8 +3,8 @@ } %healthcheck-output::before { @extend %as-pseudo; - min-width: 26px; - min-height: 26px; + min-width: 20px; + min-height: 20px; margin-right: 15px; } @media #{$--lt-spacious-healthcheck-output} { diff --git a/ui-v2/app/styles/components/table.scss b/ui-v2/app/styles/components/table.scss index 3f86247daa1e..e5090ffe2fb9 100644 --- a/ui-v2/app/styles/components/table.scss +++ b/ui-v2/app/styles/components/table.scss @@ -1,12 +1,26 @@ @import '../base/components/table/index'; @import '../base/components/popover-menu/index'; -table { - @extend %table, %table-flex; +%main-content table { + @extend %table; } - %table-actions > [type='checkbox'] { @extend %more-popover-menu; } +%more-popover-menu-panel [type='checkbox']:checked ~ * { + /* this needs to autocalculate */ + min-height: 143px; + max-height: 143px; +} +%more-popover-menu-panel [id$='logout']:checked ~ * { + /* this needs to autocalculate */ + min-height: 183px; + max-height: 183px; +} +%more-popover-menu-panel [id$='delete']:checked ~ ul label[for$='delete'] + [role='menu'], +%more-popover-menu-panel [id$='logout']:checked ~ ul label[for$='logout'] + [role='menu'], +%more-popover-menu-panel [id$='use']:checked ~ ul label[for$='use'] + [role='menu'] { + display: block; +} %table-actions .confirmation-alert { @extend %confirmation-alert; } @@ -16,18 +30,27 @@ table { right: 15px; } -html.template-service.template-list td:first-child a span, -html.template-node.template-show #services td:first-child a span, -html.template-service.template-show #instances td:first-child a span { +/*TODO: Rename this to %app-view-brand-icon or similar */ +%with-external-source-icon { + background-repeat: no-repeat; + background-size: contain; + width: 18px; + height: 18px; + + --kubernetes-icon: #{$kubernetes-logo-color-svg}; + --terraform-icon: #{$terraform-logo-color-svg}; + --nomad-icon: #{$nomad-logo-color-svg}; + --consul-icon: #{$consul-logo-color-svg}; + --aws-icon: #{$aws-logo-color-svg}; +} +html.template-node.template-show #services td:first-child a span { @extend %with-external-source-icon; float: left; margin-right: 10px; margin-top: 2px; } /* This nudges the th in for the external source icons */ -html.template-node.template-show #services th:first-child, -html.template-service.template-show #instances th:first-child, -html.template-service.template-list main th:first-child { +html.template-node.template-show #services th:first-child { text-indent: 28px; } @@ -73,18 +96,12 @@ th span em { html.template-policy.template-list tr > :nth-child(2) { display: none; } - html.template-service.template-list tr > :nth-child(2) { - display: none; - } } @media #{$--lt-wide-table} { /* hide actions on narrow screens, you can always click in do everything from there */ tr > .actions { display: none; } - html.template-service.template-list tr > :last-child { - display: none; - } html.template-node.template-show #services tr > :last-child { display: none; } diff --git a/ui-v2/app/styles/core/typography.scss b/ui-v2/app/styles/core/typography.scss index 5e9bb5662f6e..3d59f21009fc 100644 --- a/ui-v2/app/styles/core/typography.scss +++ b/ui-v2/app/styles/core/typography.scss @@ -41,7 +41,8 @@ pre code, %notice p, %flash-message p, %filter-bar input, -%phrase-editor input { +%phrase-editor input, +%tab-section section p { @extend %p1; } %menu-panel dl, @@ -79,6 +80,7 @@ pre code, %button { font-weight: $typo-weight-semibold; } +%app-view-header dt, %menu-panel dt, %route-card section dt, %route-card header:not(.short) dd, diff --git a/ui-v2/app/styles/routes/dc/services/index.scss b/ui-v2/app/styles/routes/dc/services/index.scss new file mode 100644 index 000000000000..5c6071c98653 --- /dev/null +++ b/ui-v2/app/styles/routes/dc/services/index.scss @@ -0,0 +1,17 @@ +.proxy-upstreams > ul { + @extend %proxy-upstreams-list; +} +%proxy-upstreams-list > li { + @extend %composite-row; +} +.proxy-exposed-paths tbody tr { + @extend %proxy-exposed-paths-row; + cursor: default !important; +} +%proxy-exposed-paths-row:hover { + box-shadow: none !important; +} +%proxy-exposed-paths-row .combined-address button:hover { + // In this case we do not need a background on the icon + background-color: transparent !important; +} diff --git a/ui-v2/app/templates/dc/acls/policies/index.hbs b/ui-v2/app/templates/dc/acls/policies/index.hbs index 0dd5e37f8921..10e85407ceab 100644 --- a/ui-v2/app/templates/dc/acls/policies/index.hbs +++ b/ui-v2/app/templates/dc/acls/policies/index.hbs @@ -16,9 +16,11 @@

Access Controls

- {{#if isAuthorized }} - {{partial 'dc/acls/nav'}} - {{/if}} + + +{{#if isAuthorized }} + {{partial 'dc/acls/nav'}} +{{/if}} {{partial 'dc/acls/disabled'}} diff --git a/ui-v2/app/templates/dc/acls/roles/index.hbs b/ui-v2/app/templates/dc/acls/roles/index.hbs index 88700fdca502..b809f33f14ee 100644 --- a/ui-v2/app/templates/dc/acls/roles/index.hbs +++ b/ui-v2/app/templates/dc/acls/roles/index.hbs @@ -16,9 +16,11 @@

Access Controls

- {{#if isAuthorized }} - {{partial 'dc/acls/nav'}} - {{/if}} +
+ +{{#if isAuthorized }} + {{partial 'dc/acls/nav'}} +{{/if}} {{partial 'dc/acls/disabled'}} diff --git a/ui-v2/app/templates/dc/acls/tokens/index.hbs b/ui-v2/app/templates/dc/acls/tokens/index.hbs index 25c6a2c0e180..b5208060749b 100644 --- a/ui-v2/app/templates/dc/acls/tokens/index.hbs +++ b/ui-v2/app/templates/dc/acls/tokens/index.hbs @@ -16,6 +16,8 @@

Access Controls

+
+ {{#if isAuthorized }} {{partial 'dc/acls/nav'}} {{/if}} diff --git a/ui-v2/app/templates/dc/nodes/show.hbs b/ui-v2/app/templates/dc/nodes/show.hbs index 0e3f4c157d33..c6b86c48cb61 100644 --- a/ui-v2/app/templates/dc/nodes/show.hbs +++ b/ui-v2/app/templates/dc/nodes/show.hbs @@ -14,6 +14,8 @@ {{ item.Node }} + + -
-

- {{ item.ID }} -

- -
+

+ {{ item.ID }} +

+ +
+
Service Name
{{item.Service}}
@@ -24,44 +24,19 @@
Node Name
{{item.Node.Node}}
-{{#if proxy.ServiceName}} -
-
-{{/if}} -{{#if (eq item.Kind 'connect-proxy')}} - {{#if item.Proxy.DestinationServiceID}} -
-
Dest. Service Instance
-
{{item.Proxy.DestinationServiceID}}
-
-
-
Local Service Address
-
{{item.Proxy.LocalServiceAddress}}:{{item.Proxy.LocalServicePort}}
-
- {{else}} -
-
Dest. Service
-
{{item.Proxy.DestinationServiceName}}
-
- {{/if}} -{{/if}}
-
-

- You can expose individual HTTP paths like /metrics through Envoy for external services like Prometheus. -

- - - Path - Protocol - Listener port - Local path port - Combined addressService address, listener port, and path all combined into one URL. - - - - {{path.Path}} - - - {{path.Protocol}} - - - {{path.ListenerPort}} - - - {{path.LocalPathPort}} - - - {{item.Address}}:{{path.ListenerPort}}{{path.Path}} - - - -
- diff --git a/ui-v2/app/templates/dc/services/instance/proxy.hbs b/ui-v2/app/templates/dc/services/instance/proxy.hbs new file mode 100644 index 000000000000..a3f373d7a1e8 --- /dev/null +++ b/ui-v2/app/templates/dc/services/instance/proxy.hbs @@ -0,0 +1,96 @@ +
+
+ {{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}} +
+

Proxy health checks

+ +
+ {{/if}} + {{#if (gt proxy.Proxy.Upstreams.length 0)}} +
+

Upstreams

+
    + {{#let proxy.Datacenter as |proxyDatacenter|}} + {{#each proxy.Proxy.Upstreams as |item|}} +
  • +

    + {{item.DestinationName}} +

    +
      + {{#if (env 'CONSUL_NSPACES_ENABLED')}} + {{#if (not-eq item.DestinationType 'prepared_query')}} +
    • + {{or item.DestinationNamespace 'default'}} +
    • + {{/if}} + {{/if}} + {{#if (and (not-eq item.Datacenter proxyDatacenter) (not-eq item.Datacenter ""))}} +
    • + {{item.Datacenter}} +
    • + {{/if}} + {{#if (gt item.LocalBindPort 0)}} + {{#let (concat (or item.LocalBindAddress '127.0.0.1') ':' item.LocalBindPort) as |combinedAddress| }} +
    • + + {{combinedAddress}} +
    • + {{/let}} + {{/if}} +
    +
  • + {{/each}} + {{/let}} +
+
+ {{/if}} + {{#if (gt proxy.Proxy.Upstreams.length 0)}} +
+

Exposed paths

+

+ The following list shows individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our documentation. +

+ + + + + + + + + + {{#each proxy.Proxy.Expose.Paths as |path|}} + + + + + + {{#let (concat item.Address ':' path.ListenerPort path.Path) as |combinedAddress| }} + + {{/let}} + + {{/each}} + +
PathProtocolListener portLocal path portCombined addressService address, listener port, and path all combined into one URL.
+ {{or path.Path '-'}} + + {{or path.Protocol '-'}} + + {{or path.ListenerPort '-'}} + + {{or path.LocalPathPort '-'}} + + {{#if combinedAddress}} + {{combinedAddress}} + + {{else}} + {{'-'}} + {{/if}} +
+
+ {{/if}} +
+
diff --git a/ui-v2/app/templates/dc/services/instance/upstreams.hbs b/ui-v2/app/templates/dc/services/instance/upstreams.hbs deleted file mode 100644 index e4d3fea365bb..000000000000 --- a/ui-v2/app/templates/dc/services/instance/upstreams.hbs +++ /dev/null @@ -1,43 +0,0 @@ -
-
- {{#if (gt item.Proxy.Upstreams.length 0) }} - - - Upstream - Datacenter - Type - Local Bind Address - - - - - {{item.DestinationName}} - {{#if (env 'CONSUL_NSPACES_ENABLED')}} - {{#if (not-eq item.DestinationType 'prepared_query')}} - {{! TODO: slugify }} - {{or item.DestinationNamespace 'default'}} - {{/if}} - {{/if}} - - - - {{item.Datacenter}} - - - {{item.DestinationType}} - - - {{item.LocalBindAddress}}:{{item.LocalBindPort}} - - - - {{else}} -

- There are no upstreams. -

- {{/if}} -
-
diff --git a/ui-v2/app/templates/dc/services/show.hbs b/ui-v2/app/templates/dc/services/show.hbs index 9241e0af38d6..a6636da80ac7 100644 --- a/ui-v2/app/templates/dc/services/show.hbs +++ b/ui-v2/app/templates/dc/services/show.hbs @@ -10,19 +10,19 @@
-

{{item.Service.Service}}

-
+
+ {{#if (not item.Service.Kind)}} @@ -36,4 +36,4 @@ {{outlet}} - + \ No newline at end of file diff --git a/ui-v2/tests/acceptance/dc/services/instances/gateway.feature b/ui-v2/tests/acceptance/dc/services/instances/gateway.feature index 895d6ea38185..2d0f583602af 100644 --- a/ui-v2/tests/acceptance/dc/services/instances/gateway.feature +++ b/ui-v2/tests/acceptance/dc/services/instances/gateway.feature @@ -2,6 +2,12 @@ Feature: dc / services / instances / gateway: Show Gateway Service Instance Scenario: A Gateway Service instance Given 1 datacenter model with the value "dc1" + Given 1 proxy model from yaml + --- + - ServiceProxy: + DestinationServiceName: service-1 + DestinationServiceID: ~ + --- And 1 instance model from yaml --- - Service: diff --git a/ui-v2/tests/acceptance/dc/services/instances/proxyinfo.feature b/ui-v2/tests/acceptance/dc/services/instances/proxyinfo.feature new file mode 100644 index 000000000000..69ca9ec729fc --- /dev/null +++ b/ui-v2/tests/acceptance/dc/services/instances/proxyinfo.feature @@ -0,0 +1,215 @@ +@setupApplicationTest +Feature: dc / services / instances / show: Proxy Info tab + Background: + Given 1 datacenter model with the value "dc1" + Scenario: A Service instance without a Proxy does not display Proxy Info tab + Given 1 proxy model from yaml + --- + - ServiceProxy: + DestinationServiceName: service-1 + DestinationServiceID: ~ + --- + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I don't see proxyInfo on the tabs + Scenario: A Service instance with a Proxy displays Proxy Info tab + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I see proxyInfo on the tabs + + When I click proxyInfo on the tabs + + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/proxy + And I see proxyInfoIsSelected on the tabs + @notNamespaceable + Scenario: A Proxy with health checks, upstreams, and exposed paths displays all info + Given 2 instance models from yaml + --- + - Service: + ID: service-0-with-id + Kind: consul + Node: + Node: node-0 + - Service: + ID: service-0-with-id-proxy + Kind: connect-proxy + Proxy: + DestinationServiceName: service-0 + Expose: + Checks: false + Paths: + - Path: /grpc-metrics + Protocol: grpc + LocalPathPort: 8081 + ListenerPort: 8080 + - Path: /http-metrics + Protocol: http + LocalPathPort: 8082 + ListenerPort: 8083 + - Path: /http-metrics-2 + Protocol: http + LocalPathPort: 8083 + ListenerPort: 8084 + Upstreams: + - DestinationType: service + DestinationName: service-2 + DestinationNamespace: default + LocalBindAddress: 127.0.0.1 + LocalBindPort: 1111 + - DestinationType: prepared_query + DestinationName: service-3 + LocalBindAddress: 127.0.0.1 + LocalBindPort: 1112 + Node: + Node: node-0 + Checks: + - Name: Service check + ServiceID: service-0-proxy + Output: Output of check + Status: passing + - Name: Service check + ServiceID: service-0-proxy + Output: Output of check + Status: warning + - Name: Service check + Type: http + ServiceID: service-0-proxy + Output: Output of check + Status: critical + - Name: Node check + ServiceID: "" + Output: Output of check + Status: passing + - Name: Node check + ServiceID: "" + Output: Output of check + Status: warning + - Name: Node check + ServiceID: "" + Output: Output of check + Status: critical + --- + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I see proxyInfo on the tabs + + When I click proxyInfo on the tabs + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/proxy + + And I see 6 of the proxyChecks object + + And I see 2 of the upstreams object + And I see name on the upstreams like yaml + --- + - service-2 + - service-3 + --- + Scenario: A Proxy without health checks does not display Proxy Health section + And 2 instance models from yaml + --- + - Service: + ID: service-0-with-id + Kind: consul + Node: + Node: node-0 + - Service: + ID: service-0-with-id-proxy + Kind: connect-proxy + Node: + Node: node-0 + Checks: [] + --- + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I see proxyInfo on the tabs + + When I click proxyInfo on the tabs + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/proxy + And I see 0 of the proxyChecks object + Scenario: A Proxy without upstreams does not display Upstreams section + And 2 instance models from yaml + --- + - Service: + ID: service-0-with-id + Kind: consul + Node: + Node: node-0 + - Service: + ID: service-0-with-id-proxy + Kind: connect-proxy + Proxy: + Upstreams: [] + Node: + Node: node-0 + --- + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I see proxyInfo on the tabs + + When I click proxyInfo on the tabs + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/proxy + And I see 0 of the upstreams object + Scenario: A Proxy without exposed path does not display Exposed Paths section + And 2 instance models from yaml + --- + - Service: + ID: service-0-with-id + Kind: consul + Node: + Node: node-0 + - Service: + ID: service-0-with-id-proxy + Kind: connect-proxy + Proxy: + Expose: + Checks: false + Paths: [] + Node: + Node: node-0 + --- + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I see proxyInfo on the tabs + + When I click proxyInfo on the tabs + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/proxy + And I see 0 of the exposedPaths object + + + diff --git a/ui-v2/tests/acceptance/dc/services/instances/show.feature b/ui-v2/tests/acceptance/dc/services/instances/show.feature index 10b2e8d2a217..1c28d9f181bb 100644 --- a/ui-v2/tests/acceptance/dc/services/instances/show.feature +++ b/ui-v2/tests/acceptance/dc/services/instances/show.feature @@ -11,7 +11,7 @@ Feature: dc / services / instances / show: Show Service Instance Node: Node: node-0 - Service: - ID: service-0-with-id + ID: service-1-with-id Tags: ['Tag1', 'Tag2'] Meta: consul-dashboard-url: http://url.com @@ -47,14 +47,20 @@ Feature: dc / services / instances / show: Show Service Instance Status: critical --- Scenario: A Service instance has no Proxy + Given 1 proxy model from yaml + --- + - ServiceProxy: + DestinationServiceName: service-1 + DestinationServiceID: ~ + --- When I visit the instance page for yaml --- dc: dc1 service: service-0 node: another-node - id: service-0-with-id + id: service-1-with-id --- - Then the url should be /dc1/services/service-0/instances/another-node/service-0-with-id/health-checks + Then the url should be /dc1/services/service-0/instances/another-node/service-1-with-id/health-checks Then I see externalSource like "nomad" And I don't see upstreams on the tabs @@ -71,9 +77,15 @@ Feature: dc / services / instances / show: Show Service Instance When I click metadata on the tabs And I see metadataIsSelected on the tabs And I see 3 of the metadata object - And the title should be "service-0-with-id - Consul" + And the title should be "service-1-with-id - Consul" Scenario: A Service instance warns when deregistered whilst blocking + Given 1 proxy model from yaml + --- + - ServiceProxy: + DestinationServiceName: service-1 + DestinationServiceID: ~ + --- Given settings from yaml --- consul:client: @@ -91,3 +103,19 @@ Feature: dc / services / instances / show: Show Service Instance Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks And an external edit results in 0 instance models And pause until I see the text "deregistered" in "[data-notification]" + Scenario: A Service instance without a Proxy does not display Proxy Info tab + Given 1 proxy model from yaml + --- + - ServiceProxy: + DestinationServiceName: service-1 + DestinationServiceID: ~ + --- + When I visit the instance page for yaml + --- + dc: dc1 + service: service-0 + node: node-0 + id: service-0-with-id + --- + Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks + And I don't see proxy on the tabs \ No newline at end of file diff --git a/ui-v2/tests/acceptance/steps/dc/services/instances/proxyinfo-steps.js b/ui-v2/tests/acceptance/steps/dc/services/instances/proxyinfo-steps.js new file mode 100644 index 000000000000..3231912b98b4 --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/services/instances/proxyinfo-steps.js @@ -0,0 +1,10 @@ +import steps from '../../../steps'; + +// step definitions that are shared between features should be moved to the +// tests/acceptance/steps/steps.js file + +export default function(assert) { + return steps(assert).then('I should find a file', function() { + assert.ok(true, this.step); + }); +} diff --git a/ui-v2/tests/pages/dc/services/instance.js b/ui-v2/tests/pages/dc/services/instance.js index 78ded4f780b5..309f9cde60b3 100644 --- a/ui-v2/tests/pages/dc/services/instance.js +++ b/ui-v2/tests/pages/dc/services/instance.js @@ -2,31 +2,18 @@ export default function(visitable, attribute, collection, text, tabs) { return { visit: visitable('/:dc/services/:service/instances/:node/:id'), externalSource: attribute('data-test-external-source', '[data-test-external-source]', { - scope: '.title-bar', + scope: '.title', }), - tabs: tabs('tab', [ - 'health-checks', - 'addresses', - 'upstreams', - 'exposed-paths', - 'tags', - 'metadata', - ]), - serviceChecks: collection('[data-test-service-checks] li', { - exposed: attribute('data-test-exposed', '[data-test-exposed]'), - }), - nodeChecks: collection('[data-test-node-checks] li', { - exposed: attribute('data-test-exposed', '[data-test-exposed]'), - }), - upstreams: collection('#upstreams [data-test-tabular-row]', { + tabs: tabs('tab', ['health-checks', 'proxy-info', 'addresses', 'tags', 'metadata']), + serviceChecks: collection('[data-test-service-checks] li'), + nodeChecks: collection('[data-test-node-checks] li'), + upstreams: collection('[data-test-proxy-upstreams] > li', { name: text('[data-test-destination-name]'), - datacenter: text('[data-test-destination-datacenter]'), - type: text('[data-test-destination-type]'), - address: text('[data-test-local-bind-address]'), }), - exposedPaths: collection('#exposed-paths [data-test-tabular-row]', { + exposedPaths: collection('[data-test-proxy-exposed-paths] > tbody tr', { combinedAddress: text('[data-test-combined-address]'), }), + proxyChecks: collection('[data-test-proxy-checks] li'), addresses: collection('#addresses [data-test-tabular-row]', { address: text('[data-test-address]'), }), diff --git a/ui-v2/tests/pages/dc/services/show.js b/ui-v2/tests/pages/dc/services/show.js index 86acf55b0360..e5fa7bb6d727 100644 --- a/ui-v2/tests/pages/dc/services/show.js +++ b/ui-v2/tests/pages/dc/services/show.js @@ -2,7 +2,7 @@ export default function(visitable, attribute, collection, text, intentions, filt return { visit: visitable('/:dc/services/:service'), externalSource: attribute('data-test-external-source', '[data-test-external-source]', { - scope: '.title-bar', + scope: '.title', }), dashboardAnchor: { href: attribute('href', '[data-test-dashboard-anchor]'),