From c548d9445035b9f8abd3515e31b6231dd4134f3f Mon Sep 17 00:00:00 2001 From: Kenia <19161242+kaxcode@users.noreply.github.com> Date: Tue, 12 May 2020 17:34:47 -0400 Subject: [PATCH 1/3] ui: Create Linked Services Tab for Terminating Gateways (#7858) * Fix to bottom border not applying to the correct
  • * Create Linked Services tab with styling and tests * Add internal endpoint gateway-services-nodes to the codebase with tests * Upgrade consul-api-double to version 2.15.0 --- ui-v2/app/adapters/gateway.js | 17 +++++ ui-v2/app/models/gateway.js | 12 ++++ ui-v2/app/router.js | 3 + ui-v2/app/routes/dc/services/show.js | 64 +++++++++++-------- ui-v2/app/routes/dc/services/show/services.js | 14 ++++ ui-v2/app/serializers/gateway.js | 17 +++++ ui-v2/app/services/repository/gateway.js | 8 +++ .../components/consul-service-list.scss | 2 +- .../app/styles/routes/dc/services/index.scss | 12 ++++ ui-v2/app/templates/dc/services/show.hbs | 19 ++++-- .../templates/dc/services/show/services.hbs | 34 ++++++++++ ui-v2/config/environment.js | 2 + .../dc/services/show/gateway-services.feature | 34 ++++++++++ .../services/show/gateway-services-steps.js | 10 +++ .../integration/adapters/gateway-test.js | 27 ++++++++ .../integration/serializers/gateway-test.js | 47 ++++++++++++++ .../services/repository/gateway-test.js | 42 ++++++++++++ ui-v2/tests/pages/dc/services/show.js | 5 +- ui-v2/tests/unit/adapters/gateway-test.js | 12 ++++ ui-v2/tests/unit/models/gateway-test.js | 13 ++++ ui-v2/tests/unit/serializers/gateway-test.js | 23 +++++++ .../unit/services/repository/gateway-test.js | 12 ++++ ui-v2/yarn.lock | 6 +- 23 files changed, 399 insertions(+), 36 deletions(-) create mode 100644 ui-v2/app/adapters/gateway.js create mode 100644 ui-v2/app/models/gateway.js create mode 100644 ui-v2/app/routes/dc/services/show/services.js create mode 100644 ui-v2/app/serializers/gateway.js create mode 100644 ui-v2/app/services/repository/gateway.js create mode 100644 ui-v2/app/templates/dc/services/show/services.hbs create mode 100644 ui-v2/tests/acceptance/dc/services/show/gateway-services.feature create mode 100644 ui-v2/tests/acceptance/steps/dc/services/show/gateway-services-steps.js create mode 100644 ui-v2/tests/integration/adapters/gateway-test.js create mode 100644 ui-v2/tests/integration/serializers/gateway-test.js create mode 100644 ui-v2/tests/integration/services/repository/gateway-test.js create mode 100644 ui-v2/tests/unit/adapters/gateway-test.js create mode 100644 ui-v2/tests/unit/models/gateway-test.js create mode 100644 ui-v2/tests/unit/serializers/gateway-test.js create mode 100644 ui-v2/tests/unit/services/repository/gateway-test.js diff --git a/ui-v2/app/adapters/gateway.js b/ui-v2/app/adapters/gateway.js new file mode 100644 index 000000000..96d1e4cf1 --- /dev/null +++ b/ui-v2/app/adapters/gateway.js @@ -0,0 +1,17 @@ +import Adapter from './application'; + +export default Adapter.extend({ + requestForQueryRecord: function(request, { dc, ns, index, id }) { + if (typeof id === 'undefined') { + throw new Error('You must specify an id'); + } + return request` + GET /v1/internal/ui/gateway-services-nodes/${id}?${{ dc }} + + ${{ + ...this.formatNspace(ns), + index, + }} + `; + }, +}); diff --git a/ui-v2/app/models/gateway.js b/ui-v2/app/models/gateway.js new file mode 100644 index 000000000..85b081a0f --- /dev/null +++ b/ui-v2/app/models/gateway.js @@ -0,0 +1,12 @@ +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; + +export const PRIMARY_KEY = 'uid'; +export const SLUG_KEY = 'Name'; +export default Model.extend({ + [PRIMARY_KEY]: attr('string'), + [SLUG_KEY]: attr('string'), + Datacenter: attr('string'), + Namespace: attr('string'), + Services: attr(), +}); diff --git a/ui-v2/app/router.js b/ui-v2/app/router.js index d11aa5a95..8566d03b8 100644 --- a/ui-v2/app/router.js +++ b/ui-v2/app/router.js @@ -19,6 +19,9 @@ export const routes = { intentions: { _options: { path: '/intentions' }, }, + services: { + _options: { path: '/services' }, + }, routing: { _options: { path: '/routing' }, }, diff --git a/ui-v2/app/routes/dc/services/show.js b/ui-v2/app/routes/dc/services/show.js index 0ff145fbf..a01281f55 100644 --- a/ui-v2/app/routes/dc/services/show.js +++ b/ui-v2/app/routes/dc/services/show.js @@ -8,6 +8,7 @@ export default Route.extend({ intentionRepo: service('repository/intention'), chainRepo: service('repository/discovery-chain'), proxyRepo: service('repository/proxy'), + gatewayRepo: service('repository/gateway'), settings: service('settings'), model: function(params, transition = {}) { const dc = this.modelFor('dc').dc.Name; @@ -17,32 +18,43 @@ export default Route.extend({ urls: this.settings.findBySlug('urls'), dc: dc, proxies: [], - }).then(model => { - return ['connect-proxy', 'mesh-gateway'].includes(get(model, 'item.Service.Kind')) - ? model - : hash({ - intentions: this.intentionRepo.findByService(params.name, dc, nspace), - chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) { - const code = get(e, 'errors.firstObject.status'); - // Currently we are specifically catching a 500, but we return null - // by default, so null for all errors. - // The extra code here is mainly for documentation purposes - // and for if we need to perform different actions based on the error code - // in the future - switch (code) { - case '500': - // connect is likely to be disabled - // we just return a null to hide the tab - // `Connect must be enabled in order to use this endpoint` - return null; - default: - return null; - } - }), - proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace), - ...model, - }); - }); + }) + .then(model => { + return ['connect-proxy', 'mesh-gateway', 'ingress-gateway', 'terminating-gateway'].includes( + get(model, 'item.Service.Kind') + ) + ? model + : hash({ + intentions: this.intentionRepo.findByService(params.name, dc, nspace), + chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) { + const code = get(e, 'errors.firstObject.status'); + // Currently we are specifically catching a 500, but we return null + // by default, so null for all errors. + // The extra code here is mainly for documentation purposes + // and for if we need to perform different actions based on the error code + // in the future + switch (code) { + case '500': + // connect is likely to be disabled + // we just return a null to hide the tab + // `Connect must be enabled in order to use this endpoint` + return null; + default: + return null; + } + }), + proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace), + ...model, + }); + }) + .then(model => { + return ['ingress-gateway', 'terminating-gateway'].includes(get(model, 'item.Service.Kind')) + ? hash({ + gateway: this.gatewayRepo.findBySlug(params.name, dc, nspace), + ...model, + }) + : model; + }); }, setupController: function(controller, model) { controller.setProperties(model); diff --git a/ui-v2/app/routes/dc/services/show/services.js b/ui-v2/app/routes/dc/services/show/services.js new file mode 100644 index 000000000..9793d62c0 --- /dev/null +++ b/ui-v2/app/routes/dc/services/show/services.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/serializers/gateway.js b/ui-v2/app/serializers/gateway.js new file mode 100644 index 000000000..069c7a695 --- /dev/null +++ b/ui-v2/app/serializers/gateway.js @@ -0,0 +1,17 @@ +import Serializer from './application'; +import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/gateway'; + +export default Serializer.extend({ + primaryKey: PRIMARY_KEY, + slugKey: SLUG_KEY, + respondForQueryRecord: function(respond, query) { + return this._super(function(cb) { + return respond(function(headers, body) { + return cb(headers, { + Name: query.id, + Services: body, + }); + }); + }, query); + }, +}); diff --git a/ui-v2/app/services/repository/gateway.js b/ui-v2/app/services/repository/gateway.js new file mode 100644 index 000000000..647cf8662 --- /dev/null +++ b/ui-v2/app/services/repository/gateway.js @@ -0,0 +1,8 @@ +import RepositoryService from 'consul-ui/services/repository'; + +const modelName = 'gateway'; +export default RepositoryService.extend({ + getModelName: function() { + return modelName; + }, +}); diff --git a/ui-v2/app/styles/components/consul-service-list.scss b/ui-v2/app/styles/components/consul-service-list.scss index d6d41bbbf..82c9cc3d1 100644 --- a/ui-v2/app/styles/components/consul-service-list.scss +++ b/ui-v2/app/styles/components/consul-service-list.scss @@ -1,7 +1,7 @@ .consul-service-list > ul { @extend %consul-service-list; } -%consul-service-list > li { +%consul-service-list > li:not(:first-child) { @extend %consul-service-row; } %consul-service-row { diff --git a/ui-v2/app/styles/routes/dc/services/index.scss b/ui-v2/app/styles/routes/dc/services/index.scss index 5c6071c98..ac5078f4e 100644 --- a/ui-v2/app/styles/routes/dc/services/index.scss +++ b/ui-v2/app/styles/routes/dc/services/index.scss @@ -1,3 +1,15 @@ +// Services - Linked Services tab +.gateway-services-list > ul { + @extend %gateway-services-list; +} +%gateway-services-list > li:not(:first-child) { + @extend %gateway-service-row; +} +%gateway-service-row { + @extend %composite-row, %with-composite-row-intent; +} + +// Service Detail - Proxy Info tab .proxy-upstreams > ul { @extend %proxy-upstreams-list; } diff --git a/ui-v2/app/templates/dc/services/show.hbs b/ui-v2/app/templates/dc/services/show.hbs index 0f766c90f..395b2ee71 100644 --- a/ui-v2/app/templates/dc/services/show.hbs +++ b/ui-v2/app/templates/dc/services/show.hbs @@ -17,14 +17,23 @@ - {{#if (not item.Service.Kind)}} + {{#if (or (not item.Service.Kind) (eq item.Service.Kind 'terminating-gateway'))}} {{/if}} diff --git a/ui-v2/app/templates/dc/services/show/services.hbs b/ui-v2/app/templates/dc/services/show/services.hbs new file mode 100644 index 000000000..d8558061c --- /dev/null +++ b/ui-v2/app/templates/dc/services/show/services.hbs @@ -0,0 +1,34 @@ +
    +
    + {{#if (gt gateway.Services.length 0)}} +
    +

    + The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our + step-by-step guide. + +

    + + + {{item.Name}} + +
      + {{#if (not-eq item.InstanceCount 0)}} +
    • + {{format-number item.InstanceCount}} {{pluralize item.InstanceCount 'Instance' without-count=true}} +
    • + {{/if}} + +
    • + +
    • +
      +
    +
    +
    + {{else}} +

    + There are no linked services. +

    + {{/if}} +
    +
    diff --git a/ui-v2/config/environment.js b/ui-v2/config/environment.js index f74e2bdfd..964481f1e 100644 --- a/ui-v2/config/environment.js +++ b/ui-v2/config/environment.js @@ -81,6 +81,8 @@ module.exports = function(environment, $ = process.env) { CONSUL_DOCS_LEARN_URL: 'https://learn.hashicorp.com', CONSUL_DOCS_API_URL: 'https://www.consul.io/api', CONSUL_COPYRIGHT_URL: 'https://www.hashicorp.com', + CONSUL_TERMINATING_GATEWAYS_URL: 'https://www.consul.io/docs/connect/terminating_gateway', + CONSUL_INGRESS_GATEWAYS_URL: 'https://www.consul.io/docs/connect/ingress_gateway', }); const isTestLike = ['staging', 'test'].indexOf(environment) > -1; const isDevLike = ['development', 'staging', 'test'].indexOf(environment) > -1; diff --git a/ui-v2/tests/acceptance/dc/services/show/gateway-services.feature b/ui-v2/tests/acceptance/dc/services/show/gateway-services.feature new file mode 100644 index 000000000..b394925c7 --- /dev/null +++ b/ui-v2/tests/acceptance/dc/services/show/gateway-services.feature @@ -0,0 +1,34 @@ +@setupApplicationTest +Feature: dc / services / gateway + Background: + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: terminating-gateway-1 + Kind: terminating-gateway + --- + Scenario: Seeing the Linked Services tab + When I visit the service page for yaml + --- + dc: dc1 + service: terminating-gateway-1 + --- + And the title should be "terminating-gateway-1 - Consul" + And I see linkedServices on the tabs + When I click linkedServices on the tabs + And I see linkedServicesIsSelected on the tabs + Scenario: Seeing the list of Linked Services + Given 3 service models from yaml + When I visit the service page for yaml + --- + dc: dc1 + service: terminating-gateway-1 + --- + And the title should be "terminating-gateway-1 - Consul" + When I click linkedServices on the tabs + Then I see 3 service models + + + diff --git a/ui-v2/tests/acceptance/steps/dc/services/show/gateway-services-steps.js b/ui-v2/tests/acceptance/steps/dc/services/show/gateway-services-steps.js new file mode 100644 index 000000000..3231912b9 --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/services/show/gateway-services-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/integration/adapters/gateway-test.js b/ui-v2/tests/integration/adapters/gateway-test.js new file mode 100644 index 000000000..f2138a61b --- /dev/null +++ b/ui-v2/tests/integration/adapters/gateway-test.js @@ -0,0 +1,27 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Integration | Adapter | gateway', function(hooks) { + setupTest(hooks); + const dc = 'dc-1'; + const id = 'slug'; + test('requestForQueryRecord returns the correct url/method', function(assert) { + const adapter = this.owner.lookup('adapter:gateway'); + const client = this.owner.lookup('service:client/http'); + const expected = `GET /v1/internal/ui/gateway-services-nodes/${id}?dc=${dc}`; + const actual = adapter.requestForQueryRecord(client.url, { + dc: dc, + id: id, + }); + assert.equal(actual, expected); + }); + test("requestForQueryRecord throws if you don't specify an id", function(assert) { + const adapter = this.owner.lookup('adapter:gateway'); + const client = this.owner.lookup('service:client/http'); + assert.throws(function() { + adapter.requestForQueryRecord(client.url, { + dc: dc, + }); + }); + }); +}); diff --git a/ui-v2/tests/integration/serializers/gateway-test.js b/ui-v2/tests/integration/serializers/gateway-test.js new file mode 100644 index 000000000..837c09aa7 --- /dev/null +++ b/ui-v2/tests/integration/serializers/gateway-test.js @@ -0,0 +1,47 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +import { get } from 'consul-ui/tests/helpers/api'; +import { + HEADERS_SYMBOL as META, + HEADERS_DATACENTER as DC, + HEADERS_NAMESPACE as NSPACE, +} from 'consul-ui/utils/http/consul'; + +module('Integration | Serializer | gateway', function(hooks) { + setupTest(hooks); + test('respondForQueryRecord returns the correct data for item endpoint', function(assert) { + const serializer = this.owner.lookup('serializer:gateway'); + const dc = 'dc-1'; + const id = 'slug'; + const nspace = 'default'; + const request = { + url: `/v1/internal/ui/gateway-services-nodes/${id}?dc=${dc}`, + }; + return get(request.url).then(function(payload) { + const expected = { + Datacenter: dc, + [META]: { + [DC.toLowerCase()]: dc, + [NSPACE.toLowerCase()]: nspace, + }, + uid: `["${nspace}","${dc}","${id}"]`, + Name: id, + Namespace: nspace, + Services: payload, + }; + const actual = serializer.respondForQueryRecord( + function(cb) { + const headers = {}; + const body = payload; + return cb(headers, body); + }, + { + dc: dc, + id: id, + } + ); + assert.deepEqual(actual, expected); + }); + }); +}); diff --git a/ui-v2/tests/integration/services/repository/gateway-test.js b/ui-v2/tests/integration/services/repository/gateway-test.js new file mode 100644 index 000000000..c43b2f7e4 --- /dev/null +++ b/ui-v2/tests/integration/services/repository/gateway-test.js @@ -0,0 +1,42 @@ +import { moduleFor, test } from 'ember-qunit'; +import repo from 'consul-ui/tests/helpers/repo'; + +moduleFor('service:repository/gateway', 'Integration | Repository | gateway', { + // Specify the other units that are required for this test. + integration: true, +}); +const dc = 'dc-1'; +const id = 'slug'; +const nspace = 'default'; +test('findBySlug returns the correct data for item endpoint', function(assert) { + return repo( + 'Gateway', + 'findBySlug', + this.subject(), + function retrieveStub(stub) { + return stub(`/v1/internal/ui/gateway-services-nodes/${id}`); + }, + function performTest(service) { + return service.findBySlug(id, dc); + }, + function performAssertion(actual, expected) { + assert.deepEqual( + actual, + expected(function(payload) { + return Object.assign( + {}, + { + Datacenter: dc, + Name: id, + Namespace: nspace, + uid: `["${nspace}","${dc}","${id}"]`, + }, + { + Services: payload, + } + ); + }) + ); + } + ); +}); diff --git a/ui-v2/tests/pages/dc/services/show.js b/ui-v2/tests/pages/dc/services/show.js index e5fa7bb6d..984c57255 100644 --- a/ui-v2/tests/pages/dc/services/show.js +++ b/ui-v2/tests/pages/dc/services/show.js @@ -7,7 +7,7 @@ export default function(visitable, attribute, collection, text, intentions, filt dashboardAnchor: { href: attribute('href', '[data-test-dashboard-anchor]'), }, - tabs: tabs('tab', ['instances', 'intentions', 'routing', 'tags']), + tabs: tabs('tab', ['instances', 'linked-services', 'intentions', 'routing', 'tags']), filter: filter, // TODO: These need to somehow move to subpages @@ -15,5 +15,8 @@ export default function(visitable, attribute, collection, text, intentions, filt address: text('[data-test-address]'), }), intentions: intentions(), + services: collection('.gateway-services-list> ul > li:not(:first-child)', { + name: text('[data-test-service-name]'), + }), }; } diff --git a/ui-v2/tests/unit/adapters/gateway-test.js b/ui-v2/tests/unit/adapters/gateway-test.js new file mode 100644 index 000000000..e131d648b --- /dev/null +++ b/ui-v2/tests/unit/adapters/gateway-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Adapter | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let adapter = this.owner.lookup('adapter:gateway'); + assert.ok(adapter); + }); +}); diff --git a/ui-v2/tests/unit/models/gateway-test.js b/ui-v2/tests/unit/models/gateway-test.js new file mode 100644 index 000000000..d86be7ec1 --- /dev/null +++ b/ui-v2/tests/unit/models/gateway-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('gateway', {}); + assert.ok(model); + }); +}); diff --git a/ui-v2/tests/unit/serializers/gateway-test.js b/ui-v2/tests/unit/serializers/gateway-test.js new file mode 100644 index 000000000..fc0a1c80e --- /dev/null +++ b/ui-v2/tests/unit/serializers/gateway-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('gateway'); + + assert.ok(serializer); + }); + + test('it serializes records', function(assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('gateway', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); diff --git a/ui-v2/tests/unit/services/repository/gateway-test.js b/ui-v2/tests/unit/services/repository/gateway-test.js new file mode 100644 index 000000000..ca2989eb3 --- /dev/null +++ b/ui-v2/tests/unit/services/repository/gateway-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Repository | gateway', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + const repo = this.owner.lookup('service:repository/gateway'); + assert.ok(repo); + }); +}); diff --git a/ui-v2/yarn.lock b/ui-v2/yarn.lock index eb4a17664..9adc4a319 100644 --- a/ui-v2/yarn.lock +++ b/ui-v2/yarn.lock @@ -1211,9 +1211,9 @@ js-yaml "^3.13.1" "@hashicorp/consul-api-double@^2.6.2": - version "2.14.7" - resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.14.7.tgz#80cd19461a3f3716bf76ba28bcabff842bcd9aef" - integrity sha512-QjpwvrraUswn/hFh+9lIKuA9keCGOkh1yr/cT3I6fDiw4JKLyDLaRN8bF/JNGtgoA/SsQh10L1YI3feZ7M3VKw== + version "2.15.0" + resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.0.tgz#033b3887449f92f26156b123f251f9e6e1b42e52" + integrity sha512-uPzwU/MPzPDhhsLjY3X1rKIjZDOSbqYHMGM0f6n15cDF2lEcbeBHwE0snuCvqTDn72qBVdYZvA13AQBm8XCy2A== "@hashicorp/ember-cli-api-double@^3.0.2": version "3.0.2" From 600dea8631115b63dfc385e5c2140b3f5c7376c8 Mon Sep 17 00:00:00 2001 From: Kenia <19161242+kaxcode@users.noreply.github.com> Date: Wed, 13 May 2020 11:28:11 -0400 Subject: [PATCH 2/3] ui: Create Upstreams tab for Ingress Gateways (#7865) --- ui-v2/app/router.js | 3 ++ .../app/routes/dc/services/show/upstreams.js | 14 +++++ .../components/composite-row/layout.scss | 9 ++++ .../styles/components/composite-row/skin.scss | 11 ++-- .../styles/components/list-collection.scss | 14 ++++- .../app/styles/routes/dc/services/index.scss | 8 +-- ui-v2/app/templates/dc/services/show.hbs | 5 +- .../templates/dc/services/show/services.hbs | 4 +- .../templates/dc/services/show/upstreams.hbs | 32 +++++++++++ ui-v2/config/environment.js | 2 - ...eway-services.feature => services.feature} | 25 ++++++++- .../dc/services/show/upstreams.feature | 54 +++++++++++++++++++ ...ay-services-steps.js => services-steps.js} | 0 .../steps/dc/services/show/upstreams-steps.js | 10 ++++ ui-v2/tests/pages/dc/services/show.js | 11 +++- 15 files changed, 183 insertions(+), 19 deletions(-) create mode 100644 ui-v2/app/routes/dc/services/show/upstreams.js create mode 100644 ui-v2/app/templates/dc/services/show/upstreams.hbs rename ui-v2/tests/acceptance/dc/services/show/{gateway-services.feature => services.feature} (53%) create mode 100644 ui-v2/tests/acceptance/dc/services/show/upstreams.feature rename ui-v2/tests/acceptance/steps/dc/services/show/{gateway-services-steps.js => services-steps.js} (100%) create mode 100644 ui-v2/tests/acceptance/steps/dc/services/show/upstreams-steps.js diff --git a/ui-v2/app/router.js b/ui-v2/app/router.js index 8566d03b8..794b5f98a 100644 --- a/ui-v2/app/router.js +++ b/ui-v2/app/router.js @@ -22,6 +22,9 @@ export const routes = { services: { _options: { path: '/services' }, }, + upstreams: { + _options: { path: '/upstreams' }, + }, routing: { _options: { path: '/routing' }, }, diff --git a/ui-v2/app/routes/dc/services/show/upstreams.js b/ui-v2/app/routes/dc/services/show/upstreams.js new file mode 100644 index 000000000..9793d62c0 --- /dev/null +++ b/ui-v2/app/routes/dc/services/show/upstreams.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/components/composite-row/layout.scss b/ui-v2/app/styles/components/composite-row/layout.scss index 7175b2166..7826a7b72 100644 --- a/ui-v2/app/styles/components/composite-row/layout.scss +++ b/ui-v2/app/styles/components/composite-row/layout.scss @@ -33,3 +33,12 @@ %composite-row-detail .port button:hover { background-color: transparent !important; } + +// Tooltip +%composite-row-detail .feedback-dialog-out { + left: -12px; + bottom: 12px; +} +%composite-row-detail .feedback-dialog-out::after { + left: 18px; +} diff --git a/ui-v2/app/styles/components/composite-row/skin.scss b/ui-v2/app/styles/components/composite-row/skin.scss index b80065922..3f7b6fc4d 100644 --- a/ui-v2/app/styles/components/composite-row/skin.scss +++ b/ui-v2/app/styles/components/composite-row/skin.scss @@ -1,9 +1,9 @@ %composite-row { list-style-type: none; - border-top-color: $gray-200; - border-bottom-color: transparent; - border-right-color: transparent; - border-left-color: transparent; + border-top-color: $transparent; + border-bottom-color: $gray-200; + border-right-color: $transparent; + border-left-color: $transparent; } %composite-row-intent { border-color: $gray-200; @@ -16,9 +16,6 @@ %composite-row-detail { color: $gray-500; } -%composite-row:last-child { - border-bottom-color: $gray-200; -} // Health Checks %composite-row .passing::before { diff --git a/ui-v2/app/styles/components/list-collection.scss b/ui-v2/app/styles/components/list-collection.scss index a91557d06..78505f903 100644 --- a/ui-v2/app/styles/components/list-collection.scss +++ b/ui-v2/app/styles/components/list-collection.scss @@ -1,7 +1,19 @@ .list-collection { + @extend %list-collection; height: 500px; position: relative; } -.list-collection > ul { +%list-collection > ul { + border-top: 1px solid $gray-200; overflow-x: hidden !important; } +%list-collection > ul > li:nth-child(2) .with-feedback p { + bottom: auto; + top: 24px; +} +%list-collection > ul > li:nth-child(2) p::after { + bottom: auto; + top: -10px !important; + border-bottom-width: 18px; + border-top-width: 0; +} diff --git a/ui-v2/app/styles/routes/dc/services/index.scss b/ui-v2/app/styles/routes/dc/services/index.scss index ac5078f4e..6f4f8c89f 100644 --- a/ui-v2/app/styles/routes/dc/services/index.scss +++ b/ui-v2/app/styles/routes/dc/services/index.scss @@ -1,8 +1,9 @@ // Services - Linked Services tab -.gateway-services-list > ul { - @extend %gateway-services-list; +// TODO - move this into composite-row +.consul-gateway-services-list > ul { + @extend %consul-gateway-services-list; } -%gateway-services-list > li:not(:first-child) { +%consul-gateway-services-list > li:not(:first-child) { @extend %gateway-service-row; } %gateway-service-row { @@ -12,6 +13,7 @@ // Service Detail - Proxy Info tab .proxy-upstreams > ul { @extend %proxy-upstreams-list; + border-top: 1px solid $gray-200; } %proxy-upstreams-list > li { @extend %composite-row; diff --git a/ui-v2/app/templates/dc/services/show.hbs b/ui-v2/app/templates/dc/services/show.hbs index 395b2ee71..eaa5b44cf 100644 --- a/ui-v2/app/templates/dc/services/show.hbs +++ b/ui-v2/app/templates/dc/services/show.hbs @@ -17,7 +17,7 @@
    - {{#if (or (not item.Service.Kind) (eq item.Service.Kind 'terminating-gateway'))}} + {{#if (not-eq item.Service.Kind 'mesh-gateway')}}

    The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our - step-by-step guide. + step-by-step guide.

    - + {{item.Name}} diff --git a/ui-v2/app/templates/dc/services/show/upstreams.hbs b/ui-v2/app/templates/dc/services/show/upstreams.hbs new file mode 100644 index 000000000..db02a53e8 --- /dev/null +++ b/ui-v2/app/templates/dc/services/show/upstreams.hbs @@ -0,0 +1,32 @@ +
    +
    + {{#if (gt gateway.Services.length 0)}} +
    +

    + Upstreams are services that may receive traffic from this gateway. Learn more about configuring gateways in our + documentation. +

    + + + {{item.Name}} + +
      + {{#if (not-eq item.GatewayConfig.ListenerPort 0)}} +
    • + + :{{item.GatewayConfig.ListenerPort}} +
    • + {{/if}} +
    +
    +
    + {{else}} +

    + There are no upstreams. +

    + {{/if}} +
    +
    diff --git a/ui-v2/config/environment.js b/ui-v2/config/environment.js index 964481f1e..f74e2bdfd 100644 --- a/ui-v2/config/environment.js +++ b/ui-v2/config/environment.js @@ -81,8 +81,6 @@ module.exports = function(environment, $ = process.env) { CONSUL_DOCS_LEARN_URL: 'https://learn.hashicorp.com', CONSUL_DOCS_API_URL: 'https://www.consul.io/api', CONSUL_COPYRIGHT_URL: 'https://www.hashicorp.com', - CONSUL_TERMINATING_GATEWAYS_URL: 'https://www.consul.io/docs/connect/terminating_gateway', - CONSUL_INGRESS_GATEWAYS_URL: 'https://www.consul.io/docs/connect/ingress_gateway', }); const isTestLike = ['staging', 'test'].indexOf(environment) > -1; const isDevLike = ['development', 'staging', 'test'].indexOf(environment) > -1; diff --git a/ui-v2/tests/acceptance/dc/services/show/gateway-services.feature b/ui-v2/tests/acceptance/dc/services/show/services.feature similarity index 53% rename from ui-v2/tests/acceptance/dc/services/show/gateway-services.feature rename to ui-v2/tests/acceptance/dc/services/show/services.feature index b394925c7..cc576c6c1 100644 --- a/ui-v2/tests/acceptance/dc/services/show/gateway-services.feature +++ b/ui-v2/tests/acceptance/dc/services/show/services.feature @@ -1,5 +1,5 @@ @setupApplicationTest -Feature: dc / services / gateway +Feature: dc / services / show / services Background: Given 1 datacenter model with the value "dc1" And 1 node models @@ -29,6 +29,29 @@ Feature: dc / services / gateway And the title should be "terminating-gateway-1 - Consul" When I click linkedServices on the tabs Then I see 3 service models + Scenario: Don't see the Linked Services tab + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: [Name] + Kind: [Kind] + --- + When I visit the service page for yaml + --- + dc: dc1 + service: [Name] + --- + And the title should be "[Name] - Consul" + And I don't see linkedServices on the tabs + Where: + --------------------------------------------- + | Name | Kind | + | service | ~ | + | ingress-gateway | ingress-gateway | + | mesh-gateway | mesh-gateway | + --------------------------------------------- diff --git a/ui-v2/tests/acceptance/dc/services/show/upstreams.feature b/ui-v2/tests/acceptance/dc/services/show/upstreams.feature new file mode 100644 index 000000000..4e0def920 --- /dev/null +++ b/ui-v2/tests/acceptance/dc/services/show/upstreams.feature @@ -0,0 +1,54 @@ +@setupApplicationTest +Feature: dc / services / show / upstreams + Background: + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: ingress-gateway-1 + Kind: ingress-gateway + --- + Scenario: Seeing the Upstreams tab + When I visit the service page for yaml + --- + dc: dc1 + service: ingress-gateway-1 + --- + And the title should be "ingress-gateway-1 - Consul" + And I see upstreams on the tabs + When I click upstreams on the tabs + And I see upstreamsIsSelected on the tabs + Scenario: Seeing the list of Upstreams + Given 3 service models from yaml + When I visit the service page for yaml + --- + dc: dc1 + service: ingress-gateway-1 + --- + And the title should be "ingress-gateway-1 - Consul" + When I click upstreams on the tabs + Then I see 3 service models + Scenario: Don't see the Upstreams tab + Given 1 datacenter model with the value "dc1" + And 1 node models + And 1 service model from yaml + --- + - Service: + Name: [Name] + Kind: [Kind] + --- + When I visit the service page for yaml + --- + dc: dc1 + service: [Name] + --- + And the title should be "[Name] - Consul" + And I don't see upstreams on the tabs + Where: + --------------------------------------------- + | Name | Kind | + | service | ~ | + | terminating-gateway | terminating-gateway | + | mesh-gateway | mesh-gateway | + --------------------------------------------- diff --git a/ui-v2/tests/acceptance/steps/dc/services/show/gateway-services-steps.js b/ui-v2/tests/acceptance/steps/dc/services/show/services-steps.js similarity index 100% rename from ui-v2/tests/acceptance/steps/dc/services/show/gateway-services-steps.js rename to ui-v2/tests/acceptance/steps/dc/services/show/services-steps.js diff --git a/ui-v2/tests/acceptance/steps/dc/services/show/upstreams-steps.js b/ui-v2/tests/acceptance/steps/dc/services/show/upstreams-steps.js new file mode 100644 index 000000000..3231912b9 --- /dev/null +++ b/ui-v2/tests/acceptance/steps/dc/services/show/upstreams-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/show.js b/ui-v2/tests/pages/dc/services/show.js index 984c57255..c9fd7bc30 100644 --- a/ui-v2/tests/pages/dc/services/show.js +++ b/ui-v2/tests/pages/dc/services/show.js @@ -7,7 +7,14 @@ export default function(visitable, attribute, collection, text, intentions, filt dashboardAnchor: { href: attribute('href', '[data-test-dashboard-anchor]'), }, - tabs: tabs('tab', ['instances', 'linked-services', 'intentions', 'routing', 'tags']), + tabs: tabs('tab', [ + 'instances', + 'linked-services', + 'upstreams', + 'intentions', + 'routing', + 'tags', + ]), filter: filter, // TODO: These need to somehow move to subpages @@ -15,7 +22,7 @@ export default function(visitable, attribute, collection, text, intentions, filt address: text('[data-test-address]'), }), intentions: intentions(), - services: collection('.gateway-services-list> ul > li:not(:first-child)', { + services: collection('.consul-gateway-services-list> ul > li:not(:first-child)', { name: text('[data-test-service-name]'), }), }; From c07ef3f36acec78fb3c4a3cd75785414a1bafbd8 Mon Sep 17 00:00:00 2001 From: Kenia <19161242+kaxcode@users.noreply.github.com> Date: Wed, 13 May 2020 13:11:20 -0400 Subject: [PATCH 3/3] ui: Add namespaces to gateway services (#7868) * Add namespace info to gateway Linked Services and Upstreams tabs * Upgrade consul-api-double to version 2.15.1 --- ui-v2/app/templates/dc/services/show/services.hbs | 9 +++++++++ ui-v2/app/templates/dc/services/show/upstreams.hbs | 9 +++++++++ ui-v2/yarn.lock | 6 +++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ui-v2/app/templates/dc/services/show/services.hbs b/ui-v2/app/templates/dc/services/show/services.hbs index 19ec6615c..1d2a29f48 100644 --- a/ui-v2/app/templates/dc/services/show/services.hbs +++ b/ui-v2/app/templates/dc/services/show/services.hbs @@ -7,11 +7,19 @@ step-by-step guide.

    + {{#let item.Service.Namespace as |nspace|}} {{item.Name}}
      + {{#if (env 'CONSUL_NSPACES_ENABLED')}} + {{#if (not-eq item.Namespace nspace)}} +
    • + {{item.Namespace}} +
    • + {{/if}} + {{/if}} {{#if (not-eq item.InstanceCount 0)}}
    • {{format-number item.InstanceCount}} {{pluralize item.InstanceCount 'Instance' without-count=true}} @@ -24,6 +32,7 @@
    + {{/let}} {{else}}

    diff --git a/ui-v2/app/templates/dc/services/show/upstreams.hbs b/ui-v2/app/templates/dc/services/show/upstreams.hbs index db02a53e8..61764e1a3 100644 --- a/ui-v2/app/templates/dc/services/show/upstreams.hbs +++ b/ui-v2/app/templates/dc/services/show/upstreams.hbs @@ -6,11 +6,19 @@ Upstreams are services that may receive traffic from this gateway. Learn more about configuring gateways in our documentation.

    + {{#let item.Service.Namespace as |nspace|}} {{item.Name}}
      + {{#if (env 'CONSUL_NSPACES_ENABLED')}} + {{#if (not-eq item.Namespace nspace)}} +
    • + {{item.Namespace}} +
    • + {{/if}} + {{/if}} {{#if (not-eq item.GatewayConfig.ListenerPort 0)}}
    • + {{/let}} {{else}}

      diff --git a/ui-v2/yarn.lock b/ui-v2/yarn.lock index 9adc4a319..fe780fc5f 100644 --- a/ui-v2/yarn.lock +++ b/ui-v2/yarn.lock @@ -1211,9 +1211,9 @@ js-yaml "^3.13.1" "@hashicorp/consul-api-double@^2.6.2": - version "2.15.0" - resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.0.tgz#033b3887449f92f26156b123f251f9e6e1b42e52" - integrity sha512-uPzwU/MPzPDhhsLjY3X1rKIjZDOSbqYHMGM0f6n15cDF2lEcbeBHwE0snuCvqTDn72qBVdYZvA13AQBm8XCy2A== + version "2.15.1" + resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.1.tgz#1b41c92ee7930e0bcead8283eea019b5f1238819" + integrity sha512-0q0h2krXFR5uj/A+x5WtsKVF1ltPPDrrxmX9g+SjUmeWHIcffH7qz/PCo4fdqWOPjcTXkPfBxSZwGd2uDishaQ== "@hashicorp/ember-cli-api-double@^3.0.2": version "3.0.2"